Shiro中的认证对象
- Subject 主体
访问系统的用户。主体可以是用户、程序等,进行认证的都称为主体。
- Principal 身份信息
是主体进行身份认证的标识,标识具有唯一性,如用户名、手机号、邮箱等,一个主体可以有多个身份,但必须有一个主身份。
- Credential 凭证信息
是只有主体自己知道的安全信息,如密码、证书等。
Shiro的认证过程
首先主体需要携带身份信息和凭证信息,比如用户名和密码,然后Shiro会将这些信息包装成一个令牌,也就是Token,然后再通过Shiro中的安全管理器进行认证。安全管理器再调用认证器,认证器再去调用Realm获取数据。将携带的身份信息和凭证信息与原始数据进行比对,如果一致的话就会进入应用也就是登录认证成功。
Shiro配置(重点)
引入Shiro依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
配置ShiroConfig
package boc.ljh.config;
import boc.ljh.config.shiro.realm.UserRealm;
import boc.ljh.pojo.User;
import org.apache.logging.log4j.message.StringFormattedMessage;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
//1.创建ShiroFilter 负责拦截所有请求
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//给ShiroFilter设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//配置系统受限和认证资源
Map<String,String> map = new HashMap<String,String>();
map.put("/user/addUser","authc");//authc 请求这个资源需要认证和授权
map.put("/user/loadAllUserList","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
//配置认证界面 当需要进行认证时会跳到下面的login.jsp页面。
shiroFilterFactoryBean.setLoginUrl("/WEB_INF/jsp/login.jsp");
return shiroFilterFactoryBean;
}
//2.创建安全管理器
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//给安全管理器设置Realm
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
//3.创建Realm
@Bean
public Realm getRealm(){
UserRealm userRealm = new UserRealm();
//修改凭证匹配器
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//设置加密算法为MD5
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
//设置散列次数
hashedCredentialsMatcher.setHashIterations(1024);
userRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return userRealm;
}
}
创建自定义的Realm
package boc.ljh.config.shiro.realm;
import boc.ljh.pojo.User;
import boc.ljh.service.UserService;
import boc.ljh.utils.ApplicationContextUtils;
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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.util.ObjectUtils;
//自动以Realm
public class UserRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//根据身份信息
String principal = (String) authenticationToken.getPrincipal();
//在工厂中获取bean对象
UserService userService = (UserService) ApplicationContextUtils.getBean("userServiceImpl");
User user = userService.loadUserInfoByUsername(principal);
if(!ObjectUtils.isEmpty(user)){
return new SimpleAuthenticationInfo(user.getUserName(),user.getUserPassword(), ByteSource.Util.bytes(user.getUserSalt()),this.getName());
}
return null;
}
}
生成随机盐配置
package boc.ljh.utils;
import java.util.Random;
public class SaltUtils {
//生成随机盐
public static String getSalt(int num){
char[] chars = "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz!@#$%^&*()_+-={}[],./;:\"'\\".toCharArray();
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i <num ; i++){
char aChar = chars[new Random().nextInt(chars.length)];
stringBuilder.append(aChar);
}
return stringBuilder.toString();
}
}
获取工厂中bean的配置
package boc.ljh.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 配置获取工厂中bean对象
*/
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
//根据bean名字获取工厂中指定的bean对象
public static Object getBean(String beanName){
return context.getBean(beanName);
}
}
实现用户注册功能(注册时对密码进行MD5+salt+hash散列加密)
controller
package boc.ljh.controller;
import boc.ljh.config.AppCode;
import boc.ljh.config.PaginationHelper;
import boc.ljh.config.Result;
import boc.ljh.pojo.User;
import boc.ljh.service.UserService;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.*;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.jws.soap.SOAPBinding;
@Api(tags = "用户管理")
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@ApiOperation("用户注册")
@PostMapping("/register")
@ApiOperationSupport(ignoreParameters = {
"userId",
"userAge",
"userSalt"
})
public Result register(@RequestBody User user){
Result result = new Result();
Integer num = userService.registerUserInfo(user);
if(num == 1){
result.setMessage("注册成功");
result.setStatus(200);
}else{
result.setMessage("注册失败");
result.setStatus(500);
}
return result;
}
}
dao
//用户注册
Integer registerUserInfo(User user);
mapper.xml
<insert id="registerUserInfo" parameterType="boc.ljh.pojo.User">
insert into user(user_name, user_password,user_salt) values (#{userName},#{userPassword},#{userSalt});
</insert>
service
//用户注册
Integer registerUserInfo(User user);
serviceImpl
@Override
public Integer registerUserInfo(User user) {
//1.生成随机盐
String salt = SaltUtils.getSalt(8);
//2.将随机盐保存到数据库
user.setUserSalt(salt);
//明文密码进行MD5+随机盐+hash散列处理
Md5Hash md5Hash = new Md5Hash(user.getUserPassword(),salt,1024);
user.setUserPassword(md5Hash.toHex());
Integer num = userDao.registerUserInfo(user);
return num;
}
实现用户登录功能
controller
@ApiOperation("用户登录")
@PostMapping("/login")
@ApiOperationSupport(ignoreParameters = {
"userAge",
"userId",
"userSalt"
})
public Result login(@RequestBody User user){
Result result = new Result();
Subject subject = SecurityUtils.getSubject();
try {
subject.login(new UsernamePasswordToken(user.getUserName(),user.getUserPassword()));
result.setMessage(AppCode.LOGIN_SUCCESS.message);
result.setStatus(AppCode.LOGIN_SUCCESS.code);
return result;
}catch (UnsupportedOperationException e) {
result.setMessage(AppCode.LOGIN_NAME_FALL.message);
result.setStatus(AppCode.LOGIN_NAME_FALL.code);
return result;
}catch (IncorrectCredentialsException e){
result.setMessage(AppCode.LOGIN_PASS_FALL.message);
result.setStatus(AppCode.LOGIN_PASS_FALL.code);
return result;
}
}
dao
//根据用户名获取用户信息
User loadUserInfoByUsername(String usename);
mapper.xml
<select id="loadUserInfoByUsername" resultMap="BaseResultMap">
service
//根据用户名获取用户信息
User loadUserInfoByUsername(String usename);
serviceImpl
@Override
public User loadUserInfoByUsername(String usename) {
return userDao.loadUserInfoByUsername(usename);
}
实现用户退出功能
controller
@ApiOperation("用户退出")
@PostMapping("/loginout")
public Result loginout(){
Result result = new Result();
Subject subject = SecurityUtils.getSubject();
subject.logout();
result.setStatus(200);
result.setMessage("退出成功");
return result;
}