SpringBoot整合Shiro MD5盐值加密
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Shiro的核心三大组件: Subject、SecurityManager 和 Realm
导入maven依赖
<!--整合shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
配置类MyShiroConfig
配置类中需要在spring创建三个对象
- ShiroFilterFactoryBean
- SecurityManager
- Realm
@Configuration
public class MyShiroConfig {
@Bean
public ShiroFilterFactoryBean filterFactory(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 设置默认登录的 URL,身份认证失败会访问该 URL
shiroFilterFactoryBean.setLoginUrl("/login");
// 设置成功之后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/");
// 设置未授权界面,权限认证失败会访问该 URL
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
// LinkedHashMap 是有序的,进行顺序拦截器配置
Map<String, String> filterChainMap = new LinkedHashMap<>();
/*
1). anon 可以被匿名访问
2). authc 必须认证(登录)后才可以访问
......
*/
filterChainMap.put("/login","anon");
filterChainMap.put("/","anon");
filterChainMap.put("/index","anon");
filterChainMap.put("/logout","logout");
filterChainMap.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
return shiroFilterFactoryBean;
}
shiroFilterFactoryBean 需要注入的属性:
- SecurityManager
- 登录失败、成功、未授权时跳转页面的URL
- 需要拦截或者放行的 URL:这些都放在一个 Map 中
map的第一个参数为url,第二个参数为权限
@Bean
public SecurityManager securityManager(Realm singleRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(singleRealm);
return securityManager;
}
需要为DefaultWebSecurityManager注入Realm对象
@Bean
public Realm realm(HashedCredentialsMatcher hashedCredentialsMatcher){
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return myRealm;
}
MyRealm是自己定义的,下面会介绍
需要进行MD5加密时,给AuthorizingRealm的CredentialsMatcher属性注入HashedCredentialsMatcher类型的对象,如下:
@Bean
public HashedCredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//使用MD5加密算法
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
//加密的次数
hashedCredentialsMatcher.setHashIterations(1024);
return hashedCredentialsMatcher;
}
}
自定义Realm
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//将AuthenticationToken强转为UsernamePasswordToken
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//获取客户端传来的username
String username = token.getUsername();
//通过username查找数据库里
User user = userService.getUserByUserName(username);
//如果User为null则用户不存在
if(User == null){
throw new UnknownAccountException("用户不存在!");
}
//User.getStatus为freeze则用户账号被冻结
if("freeze".equals(User.getStatus)){
throw new LockedAccountException("用户被冻结");
}
/*
登录输入用户名和密码,输入的密码会在shiro比较的时候通过MD5加密,
然后再和SimpleAuthenticationInfo里传的数据库里的真实密码进行比较(且数据库里的真实密码是已经加密过的字符串)
*/
//盐值:
ByteSource byteSource = ByteSource.Util.bytes(username);
//SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName)
return new SimpleAuthenticationInfo(username, User.getPassword, byteSource, getName());
}
}
Controller
@PostMapping("/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password){
//获取Subject对象
Subject subject = SecurityUtils.getSubject();
//判断当前用户是否已经登录
if(!subject.isAuthenticated()){
//把用户名和密码封装为 UsernamePasswordToken 对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//你可以自己设置一个标志位,然后根据这个标志位判断一下用户是否勾选了记住我,
// 如果勾选了就使用 token.setRememberMe(true) 设置为记住我。
token.setRememberMe(true);
try {
//进入到自定义的Realm类里的doGetAuthenticationInfo方法
subject.login(token);
} catch (AuthenticationException e) {
System.out.println("登录失败:" + e.getMessage());
}
}
return "index";
}