引言
这里的demo涉及到的内容可能有点多。所以就放上了源码。在编写本文的时候我修改了一些注解可能和源码不一样,代码功能部分都是一样的,
源码下载:本地下载
教程
1.创建数据库
创建数据库记得导入数据,我这里的账号是benzhu,密码是加密加盐过后的5282d562ad1f43221a173ab92c1793d2(解密后是123456);角色是admin;权限是user:update。
2.导入Maven
aop包是注解授权需要导入的包。这里只给导入的包而已;自己创建Spring Boot。
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot JDBC -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--shiro注解(aop生效) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
3.配置文件
application.properties
# 端口设置
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?&useSSL=false&serverTimezone=GMT%2B8&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#开启aop
spring.aop.proxy-target-class=true
4.配置自定义Realm
realm/CustomRealm.java
package com.benzhu.shiro.realm;
import com.benzhu.shiro.dao.UserDao;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.*;
@Component("realm")
public class CustomRealm extends AuthorizingRealm {
@Resource
private UserDao userDao;
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//重写授权方法
String username = (String) principals.getPrimaryPrincipal();
//从缓存中拿到角色数据(没有设置缓存只能再查一次数据库)
Set<String> roles = getRolesByUserName(username);
//从缓存中拿到权限数据(没有设置缓存只能再查一次数据库)
Set<String> permissions = getPermissionUserName(roles);
//返回对象
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(roles);
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
private Set<String> getPermissionUserName(Set<String> roles) {
Set<String> sets = new HashSet<>();
for (String role : roles){
List<String> permission = userDao.getPermissionByUserName(role);
for (String permis:permission) {
//这里输出可以看出运行了两次
System.out.println(permis);
sets.add(permis);
}
}
return sets;
}
private Set<String> getRolesByUserName(String username) {
List<String> roles = userDao.getRolesByUSerName(username);
Set<String> sets = new HashSet<>(roles);
return sets;
}
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//重写验证方法
//1.从主体传过来的认证信息中获取用户名
String username = (String) token.getPrincipal();
//2.通过用户名到数据库中获取凭证
String password = getPasswordByUserName(username);
if(password == null){
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,"customRealm");
//加盐验证
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("benzhu"));
return authenticationInfo;
}
private String getPasswordByUserName(String username) {
//数据库查询数据
return userDao.getUserPassWord(username);
}
}
5.编写Shiro的配置类
filter/ShiroConfiguration.java
package com.benzhu.shiro.filter;
import com.benzhu.shiro.realm.CustomRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import java.util.LinkedHashMap;
@Configuration
public class ShiroConfiguration {
@Resource(name = "realm")
private CustomRealm customRealm;
@Bean(name="shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") DefaultWebSecurityManager manager) {
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
bean.setSecurityManager(manager);
//配置登录的url和登录成功的url以及验证失败的url
bean.setLoginUrl("/login");
bean.setUnauthorizedUrl("/error");
//配置访问权限
LinkedHashMap<String, String> filterChainDefinitionMap=new LinkedHashMap<>();
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/sublogin", "anon");
filterChainDefinitionMap.put("/usererror", "anon");
filterChainDefinitionMap.put("/**","authc");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
//配置核心安全事务管理器
@Bean(name="securityManager")
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
manager.setRealm(customRealm);
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); //创建加密对象
matcher.setHashAlgorithmName("md5"); //加密的算法
matcher.setHashIterations(1);//加密次数
customRealm.setCredentialsMatcher(matcher); //放入自定义Realm
return manager;
}
//以下是开启注解支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
6.编写用户实体类
domain/User.java
package com.benzhu.shiro.domain;
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
7.编写连接数据库的接口和实现类
dao/UserDao.java
package com.benzhu.shiro.dao;
import java.util.List;
public interface UserDao {
String getUserPassWord(String username);
List<String> getRolesByUSerName(String username);
List<String> getPermissionByUserName(String username);
}
dao/UserDao.java
package com.benzhu.shiro.dao.impl;
import com.benzhu.shiro.dao.UserDao;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@Component
public class UserDaoImpl implements UserDao {
@Resource
private JdbcTemplate jdbcTemplate;
@Override
public String getUserPassWord(String username) {
String sql = "select password from users where username = ?";
List<String> list = jdbcTemplate.query(sql, new String[]{username}, new RowMapper<String>(){
@Override
public String mapRow(ResultSet resultSet, int i) throws SQLException {
return resultSet.getString("password");
}
});
if(list==null){
return null;
}
return list.get(0);
}
@Override
public List<String> getRolesByUSerName(String username) {
String sql = "select role_name from user_roles where username = ?";
List<String> list = jdbcTemplate.query(sql, new String[]{username}, new RowMapper<String>() {
@Override
public String mapRow(ResultSet resultSet, int i) throws SQLException {
return resultSet.getString("role_name");
}
});
return list;
}
@Override
public List<String> getPermissionByUserName(String username) {
String sql = "select permission from roles_permissions where role_name = ?";
List<String> list = jdbcTemplate.query(sql, new String[]{username}, new RowMapper<String>() {
@Override
public String mapRow(ResultSet resultSet, int i) throws SQLException {
return resultSet.getString("permission");
}
});
return list;
}
}
8.编写Controller
里面写了一些输出语句更好的理解Shiro的工作原理。
controller/UserController.java
package com.benzhu.shiro.controller;
import com.benzhu.shiro.domain.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller("UserController")
@RequestMapping("/")
public class UserController {
@RequestMapping(value = "login")
public String login(){
return "login";
}
@RequestMapping(value = "usererror")
public String error(){
return "usererror";
}
@RequestMapping(value = "sublogin")
public String sublogin(User user){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
try {
subject.login(token);
try {
subject.checkRole("admin");
subject.checkPermission("user:update");
//理解非注解也可以做转跳拦截之类的动作
if(subject.hasRole("admin")){
System.out.println("拥有admin角色!");
}
if(subject.isPermitted("user:update")){
System.out.println("拥有user:update权限!");
}
return "index";
}catch (UnauthorizedException exception){
System.out.println("角色授权或者权限授权失败!");
return "error";
}
}catch (AuthenticationException e){
System.out.println("认证失败!");
return "usererror";
}
}
@RequiresRoles("admin")
@RequestMapping("testroels")
@ResponseBody
public String testroels(){
return "有admin角色!";
}
@RequiresRoles("admin1")
@RequestMapping("testroels1")
@ResponseBody
public String testroels1(){
return "有admin1角色!";
}
@RequiresPermissions("user:update")
@RequestMapping("testPermission")
@ResponseBody
public String testPermission(){
return "拥有user:update权限";
}
@RequiresPermissions("user:update1")
@RequestMapping("testPermission1")
@ResponseBody
public String testPermission1(){
return "拥有user:update1权限";
}
@RequiresRoles("admin")
@RequestMapping("exit")
public String exit(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "login";
}
}
9.编写前端页面
前端页面比较多。
error.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>错误</title>
</head>
<body>
角色没有权限或者权限不足。<br>
<a href="/login">返回登陆</a>
</body>
</html>
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>首页</title>
</head>
<body>
恭喜你,登陆成功。<br>
<a href="/exit">退出</a>
</body>
</html>
login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8">
<title>登陆</title>
</head>
<body>
<form action="sublogin" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登陆">
</form>
</body>
</html>
usererror.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>错误</title>
</head>
<body>
账号或者密码错误。<br>
<a href="/login">返回登陆</a>
</body>
</html>
10.启动服务测试
1.登陆账号benzhu密码123456。
2.根据controller的路径来访问进行测试。
评论