赞
踩
**Apache Shiro™**是一个功能强大且易于使用的Java安全框架,用于执行身份验证,授权,加密和会话管理。使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移动应用程序到最大的Web和企业应用程序。
**Authentication**:身份认证/登录,验证用户是不是拥有相应的身份;
**Authorization**:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
**Session Manager**:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
**Cryptography**:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
**Subject**:主体,可以看到主体可以是任何可以与应用交互的“用户”;
**SecurityManager**:相 当 于 SpringMVC 中 的 DispatcherServlet
**Realm**:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;
应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager; 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。
springboot+mybatisPlus+shiro加密-认证-授权
~~~
--用户表
drop table if exists t_user;
create table t_user(
user_id int primary key auto_increment,
username varchar(20) not null unique,
password varchar(255) not null,
salt varchar(255) not null
);
insert into t_user(username,password,salt) values("tsk","1234","c79e3988-e93b-47ee-a344-46359567e108");
insert into t_user(username,password,salt) values("ylp","1234","c79e3988-e93b-47ee-a344-46359567e109");
--角色表
drop table if exists t_role;
create table t_role(
role_id int primary key auto_increment,
role_name varchar(200) not null unique
);
insert into t_role(role_name) values("seller"),("emp");
--权限表
drop table if exists t_permission;
create table t_permission(
permission_id int primary key auto_increment,
permission_name varchar(200) not null unique
);
insert into t_permission(permission_name) values("emp:query"),("emp:add"),("emp:delete"),("emp:update");
--用户角色表
drop table if exists t_user_role;
create table t_user_role(
user_id int references t_user(user_id),
role_id int references t_role(role_id)
);
insert into t_user_role(user_id,role_id) values(1,1),(2,2);
--角色权限表
drop table if exists t_role_permission;
create table t_role_permission(
role_id int references t_role(role_id),
permission_id int references t_permission(permission_id)
);
insert into t_role_permission(role_id, permission_id) values (1,1),(1,4),(2,1);
查询用户
select * from t_user where username="tsk";
查询角色
select role_name from t_role r
inner join t_user_role ur on r.role_id=ur.role_id
inner join t_user u on u.user_id=ur.user_id
where u.username="tsk";
查询权限
select permission_name from t_permission p
inner join t_role_permission rp on p.permission_id=rp.permission_id
inner join t_user_role ur on ur.role_id=rp.role_id
inner join t_user u on u.user_id=ur.user_id
where u.username="tsk";
~~~
~~~
<dependencies>
<!--spring web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--mybatis plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!--tomcat-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
~~~
## application.properties
~~~
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mmdb
spring.datasource.username=root
spring.datasource.password=1234
spring.thymeleaf.cache=false
~~~
~~~
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
public class User {
@TableField("user_id")
private Integer userId;
private String username;
private String password;
private String salt;
}
~~~
~~~
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_role")
public class Role {
@TableId("role_id")
private Integer roleId;
@TableField("role_name")
private String roleName;
}
~~~
~~~
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_permission")
public class Permission {
@TableId("permission_id")
private Integer permissionId;
@TableField("permission_name")
private String permissionName;
}
~~~
~~~
@Mapper
public interface UserDao extends BaseMapper<User> {
}
~~~
~~~
@Mapper
public interface RoleDao extends BaseMapper<Role> {
@Select("select role_name from t_role r " +
"inner join t_user_role ur on r.role_id=ur.role_id " +
"inner join t_user u on u.user_id=ur.user_id " +
"where u.username=#{username}")
public Set<String> findByUsername(@Param("username") String username);
}
~~~
~~~java
@Mapper
public interface PermissionDao extends BaseMapper<Permission> {
@Select("select permission_name from t_permission p " +
"inner join t_role_permission rp on p.permission_id=rp.permission_id " +
"inner join t_user_role ur on ur.role_id=rp.role_id " +
"inner join t_user u on u.user_id=ur.user_id " +
"where u.username=#{username}")
public Set<String> findByUsername(@Param("username") String username);
}
~~~
~~~java
public interface UserService {
public User findByUsername(String username);
public int add(User user);
}
~~~
~~~java
public interface RoleService {
public Set<String> findByUsername(String username);
}
~~~
~~~java
public interface PermissionService {
public Set<String> findByUsername(String username);
}
~~~
## 自定义Realm
~~~java
/**
* 自定义Realm
*/
public class MyRealm extends AuthorizingRealm {
@Resource
private UserService userService;
@Resource
private RoleService roleService;
@Resource
private PermissionService permissionService;
/*授权*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取用户名
String username = (String)principals.getPrimaryPrincipal();
Set<String> roles = roleService.findByUsername(username);
Set<String> permissions = permissionService.findByUsername(username);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
simpleAuthorizationInfo.addStringPermissions(permissions);
return simpleAuthorizationInfo;
}
/*认证*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取用户名
String username = (String)token.getPrincipal();
User user = userService.findByUsername(username);
if(user == null){
return null;
}
return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),this.getName());
}
}
~~~
~~~java
@Configuration
public class ShiroConfig {
//声明密码比对器
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
hashedCredentialsMatcher.setHashIterations(SpringTool.SHIRO_JM_COUNT);// 设置加密次数
hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false);
return hashedCredentialsMatcher;
}
//将自己的验证方式(Realm)加入到容器
@Bean
public MyRealm myRealm(){
MyRealm myRealm = new MyRealm();
//配置自定义密码比较器
myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myRealm;
}
//给SecurityManager注入自定义Realm
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm());
return securityManager;
}
//Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String,String> map = new HashMap<>();
//登出
map.put("/logout","logout");
//user/login允许匿名访问
map.put("/user/login","anon");
//user/query必须认证才能访问
map.put("/user/query","authc");
//user/delete必须认证而且是管理员角色才能访问
map.put("/user/delete","roles[seller]");
//...
//未登陆跳转到对应的地址
shiroFilterFactoryBean.setLoginUrl("/user/login");
//角色和权限不足跳转到对应的地址
shiroFilterFactoryBean.setUnauthorizedUrl("/user/error");
//登出跳转到对应的地址
shiroFilterFactoryBean.setSuccessUrl("/user/login");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
}
~~~
~~~java
@Controller
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
/*去登陆页面*/
@GetMapping("/login")
public String login(){
return "login";
}
/*去注册页面*/
@GetMapping("/register")
public String register(){
return "register";
}
/*用户注册*/
@PostMapping("/register")
public String register(User user){
String salt = UUID.randomUUID().toString();
user.setSalt(salt);
String password = new Md5Hash(user.getPassword(),salt, SpringTool.SHIRO_JM_COUNT).toBase64();
user.setPassword(password);
int rows = userService.add(user);
if(rows > 0){
return "login";
}else {
return "register";
}
}
/*用户登陆*/
@PostMapping("/login")
public String login(User user){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
subject.login(token);
return "main";
}
}
~~~
~~~html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.rog">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<h1>--注册--</h1>
<form action="register" method="post">
<p>用户名:<input name="username"></p>
<p>密 码:<input type="password" name="password"></p>
<p><input type="submit" value="注册"></p>
</form>
</body>
</html>
~~~
`在登录后,可以将用户存在cookie中,下次访问时,可以先不登录,就可以识别身份。在确实需要身份认证时,比如购买,支付或者其他一些重要操作时,再要求用户登录即可,用户体验好。由于可以保持用户信息,系统后台也可以更好的监控、记录用户行为,积累数据。
记住我在登录时刻:Subject.login(token);确定是否要记住我,由登录时的token控制:token.setRememberMe(true);
~~~java
/*用户登陆*/
@PostMapping("/login")
public String login(User user){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
//记住我
token.setRememberMe(true);
subject.login(token);
return "main";
}
~~~
上述使用的是默认的cookie,在网页的控制台可以看到默认记住365天。如果想修改cookie的相关配置,则修改shiro的配置类即可
~~~java
// 自定义cookie配置
@Bean
public SimpleCookie simpleCookie(){
//这个参数是cookie的名称
SimpleCookie cookie = new SimpleCookie("rememberMe");
cookie.setHttpOnly(true);
//单位是秒,7天
cookie.setMaxAge(604800);
return cookie;
}
// cookie管理对象;记住我功能,rememberMe管理器
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(simpleCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
//给SecurityManager注入自定义组件
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//给SecurityManager注入自定义realm
securityManager.setRealm(myRealm());
//给SecurityManager注入自定义rememberMe管理器
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
~~~
## 记住我前端标签
因为使用的是thymeleaf模板,比较麻烦些,使用jsp就方便多了
1、pom.xml
~~~xml
<!--html中使用shiro标签-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
~~~
2、shiro配置类中配置方言
~~~java
// 配置shiro方言,thymeleaf页面对shiro标签的支持
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
~~~
3、页面使用
~~~html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.rog" xmlns:shiro="" target="_blank">http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>主页</title>
</head>
<body>
<h1>个人主页</h1>
<p shiro:user="">
Hello, <span shiro:principal=""></span>
</p>
</body>
</html>
~~~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。