Shiro
认证丶授权丶会话管理和加密等功能.
shiro不提供和维护用户/权限,而是通过realm让开发人员自己注入
一、认证
在shiro中,认证指的是识别和证明操作者是一个合法用户
添加依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
方法一:
在resource目录下新建文件配置认证信息’
[users]
admin=123456
czkt=111111
编写测试
@Test
public void testShiro() {
IniRealm realm = new IniRealm("classpath:shiro.ini");
DefaultSecurityManager securityManager = new DefaultSecurityManager();
securityManager.setRealm(realm);
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
subject.login(token);
System.out.println("是否通过:" + subject.isAuthenticated());
}
2.springboot+shiro认证
UserRepository 添加findUserByUserName方法
public interface UserRepository extends JpaRepository<User,Integer>,JpaSpecificationExecutor<User> {
public List<User> findUsersByUserNameAndUserPassword(String userName,String userPassword);
public User findUserByUserName(String userName);
}
UserService 接口 新增getUserByUserName
public interface UserService {
public User getUserByUserName(String userName);
}
UserServiceImpl实现方法
public User getUserByUserName(String userName) {
return userRepository.findUserByUserName(userName);
}
二丶自定义Realm
在config包下创建自定义realm对象MyShiroRealm:
注意导包一点要正确,不然会发生bean注入错误
import com.aiweiyi.crm.pojo.User;
import com.aiweiyi.crm.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import javax.annotation.Resource;
public class MyShiroRealm extends AuthorizingRealm {
@Resource
private UserService userService;
//权限控制方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User)principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
return info;
}
//认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String userName = token.getUsername();
User user = userService.getUserByUserName(userName);
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getUserPassword(),getName());
return info;
}
}
在config包下创建配置对象ShiroConfig:
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfig {
@Bean
public MyShiroRealm myShiroRealm(){
MyShiroRealm shiroRealm = new MyShiroRealm();
return shiroRealm;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
shiroFilterFactory.setSecurityManager(securityManager);
return shiroFilterFactory;
}
}
重写login方法
@RequestMapping("/dologin")
public String dologin(String userName, String userPassword, HttpServletRequest request) {
// User user = userService.login(userName, userPassword);
// if (user != null) {
// request.getSession().setAttribute("loginUser", user);
// return "main";
// } else {
//
// return "login";
// }
try{
UsernamePasswordToken token = new UsernamePasswordToken(userName,userPassword);
Subject subject = SecurityUtils.getSubject();
subject.login(token);
User user = (User)subject.getPrincipal();
request.getSession().setAttribute("loginUser", user);
return "main";
}catch (UnknownAccountException | IncorrectCredentialsException e){
request.setAttribute("error", "1用户名或密码错误!");
return "login";
}
}
@RequestMapping("/logout")
public String logout(HttpSession session) {
session.removeAttribute("loginUser");
SecurityUtils.getSubject().logout(); //Shiro注销
return "login";
}
三丶授权
修改上面配置文件
shiro.ini
[users]
admin=123456,管理员
czkt=111111,经理
[roles]
管理员=user:*,role:*
经理=user:list,user:view
测试
@Test
public void testShiro() {
IniRealm realm = new IniRealm("classpath:shiro.ini");
DefaultSecurityManager securityManager = new DefaultSecurityManager();
securityManager.setRealm(realm);
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
subject.login(token);
System.out.println("是否通过:" + subject.isAuthenticated());
System.out.println("是否为管理员角色:"+subject.hasRole("管理员"));
System.out.println("拥有权限:"+subject.isPermitted("user:view"));
subject.checkPermission("user:view");
}
授权支持四种方式
1.代码级别权限控制
2.页面标签权限控制
3.方法注解权限控制
4.url拦截权限控制
静态授权
修改自定义realm授权
public class MyShiroRealm extends AuthorizingRealm{
@Resource
private IUserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("调用MyShiroRealm.doGetAuthorizationInfo获取权限信息");
User user=(User)principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//静态授权
info.addRole(user.getRole().getRoleName());
info.addStringPermission("用户列表");
if (user.getRole().getRoleName().equals("管理员")){
info.addStringPermission("用户添加");
info.addStringPermission("用户编辑");
info.addStringPermission("用户删除");
}
return info;
}
配置shiro拦截
@Bean
public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
shiroFilterFactory.setSecurityManager(securityManager);
shiroFilterFactory.setLoginUrl("/login");
shiroFilterFactory.setSuccessUrl("/main");
shiroFilterFactory.setUnauthorizedUrl("/403");
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/images/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/localcss/**", "anon");
filterChainDefinitionMap.put("/localjs/**", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/dologin", "anon");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/user/list", "perms[用户列表]");
filterChainDefinitionMap.put("/user/add", "perms[用户添加]");
filterChainDefinitionMap.put("/user/del", "perms[用户删除]");
filterChainDefinitionMap.put("/user/updateUser", "perms[用户修改]");
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactory.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactory;
}
动态授权
1 添加实体类
@Entity
@Table(name = "sys_right")
@JsonIgnoreProperties(value = {"hibernateLazyInitializer","handler"})
public class Right implements Serializable {
@Id
@Column(name = "right_code")
private String rightCode;
@Column(name = "right_parent_code")
private String rightParentCode;
@Column(name = "right_type")
private String rightType;
@Column(name = "right_test")
private String rightTest;
@Column(name = "right_url")
private String rightUrl;
@Column(name = "right_tip")
private String rightTip;
//多对多
@ManyToMany(targetEntity = Role.class,mappedBy = "rights")
@JsonIgnore
private Set<Role> roles = new HashSet<Role>(0);
//省略get/set方法,改造方法
}
2.修改Role实体类,添加与Right类的多对多关联``
@ManyToMany(targetEntity = Right.class, fetch = FetchType.EAGER)
@JoinTable(name = "sys_role_right", joinColumns = {@JoinColumn(name = "rf_role_id")}, inverseJoinColumns = {@JoinColumn(name = "rf_right_code")})
@OrderBy(value = "rightCode")
private Set<Right> rights = new HashSet<Right>(0);
3.开发RightRepository
public interface RightRepository extends JpaRepository<Right,String> {
public List<Right> findRightsByRolesOrderByRightCode(Role role);
}
4.在RoleService 接口中添加新的方法
public interface RoleService {
public List<Role> findAllRoles();
public List<Right> findAllRights();
public List<Right> findRightsByRole(Role role);
}
5.调整controller
@RequestMapping("/dologin")
public String dologin(String userName, String userPassword, HttpServletRequest request) {
// User user = userService.login(userName, userPassword);
// if (user != null) {
// request.getSession().setAttribute("loginUser", user);
// return "main";
// } else {
//
// return "login";
// }
try{
UsernamePasswordToken token = new UsernamePasswordToken(userName,userPassword);
Subject subject = SecurityUtils.getSubject();
subject.login(token);
User user = (User)subject.getPrincipal();
Role role = user.getRole();
List<Right> rights =roleService.findRightsByRole(role);
role.getRights().addAll(rights);
request.getSession().setAttribute("loginUser", user);
return "redirect:/main";
}catch (UnknownAccountException | IncorrectCredentialsException e){
request.setAttribute("error", "用户名或密码错误!");
return "login";
}
}
6.调整自定义realm
@Bean
public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
shiroFilterFactory.setSecurityManager(securityManager);
shiroFilterFactory.setLoginUrl("/login");
shiroFilterFactory.setSuccessUrl("/main");
shiroFilterFactory.setUnauthorizedUrl("/403");
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/images/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/localcss/**", "anon");
filterChainDefinitionMap.put("/localjs/**", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/dologin", "anon");
filterChainDefinitionMap.put("/logout", "logout");
// filterChainDefinitionMap.put("/user/list", "perms[用户列表]");
// filterChainDefinitionMap.put("/user/add", "perms[用户添加]");
// filterChainDefinitionMap.put("/user/del", "perms[用户删除]");
// filterChainDefinitionMap.put("/user/updateUser", "perms[用户修改]");
List<Right> rights = roleService.findAllRights();
for (Right right : rights) {
if (right.getRightUrl() != null && !right.getRightUrl().trim().equals("")) {
filterChainDefinitionMap.put(right.getRightUrl(), "perms[" + right.getRightCode() + "]");
}
}
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactory.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactory;
}
四丶会话管理和加密
1.会话相关API
Subject.getSession():获取会话
session.setAttribute(key,val):设置会话属性
session.getAttribute(key):获取会话属性
session.removeAttribute(key):删除会话属性
Shiro提供SessionDAO用于会话持久化,提供crud操作
在开发中,建议在Controller层使用原生的HttpSession对象,在Service层中使用Shiro提供的session对象.
2.缓存
设置缓存```java
package com.aiweiyi.crm.config.shiro;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.aiweiyi.crm.pojo.Right;
import com.aiweiyi.crm.service.RoleService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
@Resource
private RoleService roleService;
@Bean
public DefaultAdvisorAutoProxyCreator autoProxyCreator() {
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setProxyTargetClass(true);
return autoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
public RedisManager redisManager(){
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
redisManager.setTimeout(timeout);
return redisManager;
}
public RedisCacheManager cacheManager(){
RedisCacheManager cacheManager = new RedisCacheManager();
cacheManager.setRedisManager(redisManager());
cacheManager.setPrincipalIdFieldName("userName");
cacheManager.setExpire(1800);
return cacheManager;
}
private RedisSessionDAO redisSessionDAO(){
RedisSessionDAO sessionDAO = new RedisSessionDAO();
sessionDAO.setRedisManager(redisManager());
return sessionDAO;
}
private DefaultWebSessionManager sessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm shiroRealm = new MyShiroRealm();
shiroRealm.setCachingEnabled(true);
shiroRealm.setAuthorizationCachingEnabled(true);
shiroRealm.setAuthorizationCacheName("authorizationCache");
shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return shiroRealm;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
securityManager.setCacheManager(cacheManager());
securityManager.setSessionManager(sessionManager());
// SecurityUtils.setSecurityManager(securityManager);
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
shiroFilterFactory.setSecurityManager(securityManager);
shiroFilterFactory.setLoginUrl("/login");
shiroFilterFactory.setSuccessUrl("/main");
shiroFilterFactory.setUnauthorizedUrl("/403");
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/images/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/localcss/**", "anon");
filterChainDefinitionMap.put("/localjs/**", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/dologin", "anon");
filterChainDefinitionMap.put("/logout", "logout");
// filterChainDefinitionMap.put("/user/list", “perms[用户列表]”);
// filterChainDefinitionMap.put("/user/add", “perms[用户添加]”);
// filterChainDefinitionMap.put("/user/del", “perms[用户删除]”);
// filterChainDefinitionMap.put("/user/updateUser", “perms[用户修改]”);
List rights = roleService.findAllRights();
for (Right right : rights) {
if (right.getRightUrl() != null && !right.getRightUrl().trim().equals("")) {
filterChainDefinitionMap.put(right.getRightUrl(), “perms[” + right.getRightCode() + “]”);
}
}
filterChainDefinitionMap.put("/**", “authc”);
shiroFilterFactory.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactory;
}
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//md5加密
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//设置加密次数
hashedCredentialsMatcher.setHashIterations(2);
// hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
return hashedCredentialsMatcher;
}
}
# 加密
1.哈希与盐
特点:
原始密码经过哈希函数计算后得到一个哈希值
改变原始密码,哈希函数计算出的哈希值也会相应改变
同样的密码,哈希值也是相同的
```java
@Test
public void testMd5Hash(){
Md5Hash md5=new Md5Hash("adming","czkt",3);
System.out.println(md5.toString());
}
package com.aiweiyi.crm.config.shiro;
import com.aiweiyi.crm.pojo.Right;
import com.aiweiyi.crm.pojo.Role;
import com.aiweiyi.crm.pojo.User;
import com.aiweiyi.crm.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import javax.annotation.Resource;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class MyShiroRealm extends AuthorizingRealm {
@Resource
private UserService userService;
@Resource
private StringRedisTemplate stringRedisTemplate;
private String SHIRO_LOGIN_COUNT = "shiro_login_count"; //登录次数
private String SHIRO_IS_LOCK = "shiro_is_lock"; //登录次数
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Role role = user.getRole();
if (role != null) {
info.addRole(user.getRole().getRoleName());
Set<Right> rights = role.getRights();
if (rights != null && rights.size() > 0) {
for (Right right : rights) {
info.addStringPermission(right.getRightCode());
}
}
}
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String userName = token.getUsername();
ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
opsForValue.increment(SHIRO_LOGIN_COUNT + userName, 1);
if (Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT + userName)) > 2) {
opsForValue.set(SHIRO_IS_LOCK + userName, "LOCK");
stringRedisTemplate.expire(SHIRO_IS_LOCK + userName, 1, TimeUnit.HOURS);
stringRedisTemplate.delete(SHIRO_LOGIN_COUNT + userName);
}
if ("LOCK".equals(opsForValue.get(SHIRO_IS_LOCK + userName))) {
throw new DisabledAccountException();
}
User user = userService.getUserByUserName(userName);
if (user == null) {
throw new UnknownAccountException();
}
if (user.getUserFlag() == null || user.getUserFlag().intValue() == 0) {
throw new LockedAccountException();
}
// SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getUserPassword(), getName());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getUserPassword(), ByteSource.Util.bytes("czkt"), getName());
return info;
}
}