在spring boot中使用Shiro做简单的登陆验证与免密码登陆的写法
- Spring boot中的配置
配置自定义ShiroRealm
@Configuration
public class ShiroConfig {
/**
* 自定义的Realm
*/
@Bean(name = "myShiroRealm")
public ShiroRealm myShiroRealm(HashedCredentialsMatcher matcher){
ShiroRealm myShiroRealm = new ShiroRealm();
myShiroRealm.setCredentialsMatcher(matcher);
return myShiroRealm;
}
@Bean
public SecurityManager securityManager(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm(matcher));
// 自定义缓存实现 使用redis
securityManager.setCacheManager(cacheManager());
// 自定义session管理 使用redis
securityManager.setSessionManager(sessionManager());
return securityManager;
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 没有登陆的用户只能访问登陆页面
shiroFilterFactoryBean.setLoginUrl("/page/index/login.html");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/page/index/login.html");
//自定义拦截器
Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
//限制同一帐号同时在线的个数。
filtersMap.put("kickout", kickoutSessionControlFilter());
shiroFilterFactoryBean.setFilters(filtersMap);
return shiroFilterFactoryBean;
}
/**
* 密码匹配凭证管理器
*
* @return
*/
@Bean(name = "hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 采用MD5方式加密
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
// 设置加密次数
hashedCredentialsMatcher.setHashIterations(Constant.MEGABYTE);
return hashedCredentialsMatcher;
}
/**
* cacheManager 缓存 redis实现
* 使用的是shiro-redis开源插件
*
* @return
*/
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
/**
* 配置shiro redisManager
* 使用的是shiro-redis开源插件
*
* @return
*/
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost("localhost");
redisManager.setPort(6379);
redisManager.setExpire(1800);// 配置缓存过期时间
redisManager.setTimeout(0);
// redisManager.setPassword(password);
return redisManager;
}
/**
* Session Manager
* 使用的是shiro-redis开源插件
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
/**
* RedisSessionDAO shiro sessionDao层的实现 通过redis
* 使用的是shiro-redis开源插件
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
/**
* 限制同一账号登录同时登录人数控制
*
* @return
*/
@Bean
public KickoutSessionControlFilter kickoutSessionControlFilter() {
KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
kickoutSessionControlFilter.setCacheManager(cacheManager());
kickoutSessionControlFilter.setSessionManager(sessionManager());
kickoutSessionControlFilter.setKickoutAfter(false);
kickoutSessionControlFilter.setMaxSession(1);
/*kickoutSessionControlFilter.setKickoutUrl("/page/index");*/
return kickoutSessionControlFilter;
}
/***
* 授权所用配置
*
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
/***
* 使授权注解起作用不如不想配置可以在pom文件中加入
* <dependency>
*<groupId>org.springframework.boot</groupId>
*<artifactId>spring-boot-starter-aop</artifactId>
*</dependency>
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* Shiro生命周期处理器
*
*/
@Bean
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
}
public class ShiroRealm extends AuthorizingRealm {
@Autowired
UserMapper userMapper;
@Autowired
InvokeRedis invokeRedis;
private AuthenticationInfo info = null;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
return authorizationInfo;
}
/**
* 获取登录账号
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 将token装换成UsernamePasswordToken
/*UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;*/
CustomToken upToken = (CustomToken) authenticationToken;
// 获取用户名即可
String username = upToken.getUsername();
// 查询数据库,是否查询到用户名和密码的用户
User userInfo = userMapper.selectByUserName(username);
if(userInfo != null) {
// 如果查询到了,封装查询结果,返回给我们的调用
Object principal = userInfo.getuName();
Object credentials = userInfo.getuPwd();
// 获取盐值,即用户名
ByteSource salt = ByteSource.Util.bytes(userInfo.getuName());
String realmName = this.getName();
// 将账户名,密码,盐值,realmName实例化到SimpleAuthenticationInfo中交给Shiro来管理
info = new SimpleAuthenticationInfo(principal, credentials, salt,realmName);
}else {
return null;
}
return info;
}
}
登陆与注册接口
@Override
public Result<String> login(String username, String password) {
// 创建Subject实例
Subject currentUser = SecurityUtils.getSubject();
// 将用户名及密码封装到UsernamePasswordToken
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
currentUser.login(token);
} catch ( UnknownAccountException e ) {
return ResultGenerator.genFailResult("登录失败,用户名不存在");
} catch ( IncorrectCredentialsException e ) {
return ResultGenerator.genFailResult("用户名密码错误");
}
return ResultGenerator.genSuccessResult("登录成功");
}
/**
* 注册
* @param username
* @param password
* @return
*/
@Override
public Result<String> register(String username, String password) {
//注册接口
ByteSource salt = ByteSource.Util.bytes(username);
String newPs = new SimpleHash("MD5", password,salt, Constant.MEGABYTE).toHex();
User user = new User.Builder().uId(Generate.flowNumber()).uName(username).uPwd(newPs).uStatus(Constant.YES).builder();
// 看数据库中是否存在该账户
if(null == userMapper.selectByUserName(username)){
if(userMapper.insertSelective(user) > 0){
return ResultGenerator.genSuccessResult("注册成功");
}
return ResultGenerator.genFailResult("注册失败");
}
return ResultGenerator.genFailResult("注册失败,账号已经被注册");
}
- 免密码登陆
在某些情况只需要验证他的用户信息而不用验证密码,此时会遇到一个问题,当不输入密码登陆时,从数据库查出的密码是最终保存的密码,没有办法通过shiro的密码验证。所以需要重写HashedCredentialsMatcher类中的doCredentialsMatch()与重写UsernamePasswordToken 类,将里面的token换成自定义的token,再将这个自定义token中加入一个标识,来表示是否需要使用密码验证。
public enum LoginType {
PASSWORD("password"), // 密码登录
NOPASSWD("nopassword"); // 免密登录
private String code;// 状态值
private LoginType(String code) {
this.code = code;
}
public String getCode () {
return code;
}
}
public class CustomToken extends UsernamePasswordToken {
private static final long serialVersionUID = -2564928913725078138L;
private LoginType type;
public CustomToken() {
super();
}
public CustomToken(String username, String password, LoginType type, boolean rememberMe, String host) {
super(username, password, rememberMe, host);
this.type = type;
}
/**免密登录*/
public CustomToken(String username) {
super(username, "", false, null);
this.type = LoginType.NOPASSWD;
}
/**账号密码登录*/
public CustomToken(String username, String pwd) {
super(username, pwd, false, null);
this.type = LoginType.PASSWORD;
}
public LoginType getType() {
return type;
}
public void setType(LoginType type) {
this.type = type;
}
}
@Configuration
public class MyRetryLimitCredentialsMatcher extends HashedCredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
CustomToken tk = (CustomToken) authcToken;
if(tk.getType().equals(LoginType.NOPASSWD)){
return true;
}
boolean matches = super.doCredentialsMatch(authcToken, info);
return matches;
}
}
还需要修改 ShiroConfig类中的配置文件
/**
* 自定义的Realm 将参数HashedCredentialsMatcher修改成重写后的类
*/
@Bean(name = "myShiroRealm")
public ShiroRealm myShiroRealm(MyRetryLimitCredentialsMatcher matcher){
ShiroRealm myShiroRealm = new ShiroRealm();
myShiroRealm.setCredentialsMatcher(matcher);
return myShiroRealm;
}
@Bean
public SecurityManager securityManager(@Qualifier("myRetryLimitCredentialsMatcher") MyRetryLimitCredentialsMatcher matcher){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm(matcher));
// 自定义缓存实现 使用redis
securityManager.setCacheManager(cacheManager());
// 自定义session管理 使用redis
securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* 密码匹配凭证管理器
*
* @return
*/
@Bean(name = "myRetryLimitCredentialsMatcher")
public MyRetryLimitCredentialsMatcher hashedCredentialsMatcher() {
MyRetryLimitCredentialsMatcher hashedCredentialsMatcher = new MyRetryLimitCredentialsMatcher();
// 采用MD5方式加密
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
// 设置加密次数
hashedCredentialsMatcher.setHashIterations(Constant.MEGABYTE);
return hashedCredentialsMatcher;
}
免密码登陆
@Override
public Result<String> wxLogin(String weChat) {
User us = userMapper.selectByUserWXId(weChat);
if(Verification.isEmpty(us)){
return ResultGenerator.genFailResult("登录失败,没有绑定手机号");
}
// 创建Subject实例
Subject currentUser = SecurityUtils.getSubject();
// 将用户名及密码封装到UsernamePasswordToken
CustomToken token = new CustomToken(us.getuName());
try {
currentUser.login(token);
} catch ( UnknownAccountException e ) {
return ResultGenerator.genFailResult("登录失败,用户名不存在");
} catch ( IncorrectCredentialsException e ) {
return ResultGenerator.genFailResult("用户名密码错误");
}
User userInfo = userMapper.selectByUserName(us.getuName());
integralService.save(userInfo.getuId(),1);
System.out.println(currentUser.getSession().getId());
return ResultGenerator.genSuccessResult("登录成功");
}