1.首先实现用户注册,在service层实现对明文密码进行md5加密
//用户注册的控制器方法
@RequestMapping(value="/adduser")
@ResponseBody
public Map addUser(HttpServletRequest request) {
String username=HttpServletRequestUtil.getString(request,"username");
String password=HttpServletRequestUtil.getString(request,"password");
TbPersonInfo tbpersonInfo=new TbPersonInfo();
tbpersonInfo.setName(username);
tbpersonInfo.setPassword(password);
int affect=personInfoService.toRegister(tbpersonInfo);
Map modelmap=new HashMap();
if(affect>=1){
modelmap.put("success",true);
}else {
modelmap.put("success",false);
}
return modelmap;
}
//业务层实现对明文密码的加密
public Integer toRegister(TbPersonInfo user){
/**
* 明文密码进行md5+salt+hash散列
* Md5Hash md5hash=new Md5Hash(password,""salt",hashIterations:8)
* */
String salt= SaltUtil.getSalt(8);
//注册时,业务层方法需要修改,将随机盐保存到uer对象中,并一同保存到数据库
String encodedPassword =SaltUtil.shiroPassword(user.getPassword(),salt);
System.out.println("业务层输出salt"+salt);
System.out.println("业务层输出password"+encodedPassword);
user.setSalt(salt);
user.setPassword(encodedPassword);
return tbPersonInfoDAO.InsertPersonInfo(user);
}
/***
* 对用户的密码进行MD5加密
* 做成工具类
*/
public static String shiroPassword(String password,String salt) {
// shiro 自带的工具类生成salt
//String salt = new SecureRandomNumberGenerator().nextBytes().toString();
// 加密次数
int times = 5;
// 算法名称
String algorithmName = "md5";
String encodedPassword = new SimpleHash(algorithmName,password,salt,times).toString();
// 返回加密后的密码
return encodedPassword;
}
/**
* 自定义生成随即盐salt的静态方法
* */
public static String getSalt(int n){
char[] chars="ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789!@#$%^&*()".toCharArray();
StringBuilder stringBuilder=new StringBuilder();
//循环,每次取一个随机字符
for(int i=0;i<n;i++){
char achar=chars[new Random().nextInt(chars.length)];
stringBuilder.append(achar);
}
return stringBuilder.toString();
}
2.shiro配置类和自定义Realm
@Configuration
public class ShiroConfig {
/* *
* Shiro内置过滤器,可以实现权限相关的拦截器
* anon 无需认证(登录)即可以访问
* authc:必须认证才可以访问
* user :如果使用rememberMe的功能可以直接访问
* perms: 该资源必须得到资源权限才可以访问
* role 该资源必须得到角色权限才可以访问
* layout 注销,直接跳转到登录页面
* */
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManage){
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManage);
//设置默认认证界面(一般是登录界面)
shiroFilterFactoryBean.setLoginUrl("/o2o/login");
// 设置成功之后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/o2o/tologin");
// 设置未授权界面,权限认证失败会访问该 URL
shiroFilterFactoryBean.setUnauthorizedUrl("/o2o/login");
// LinkedHashMap 是有序的,进行顺序拦截器配置
Map<String,String> filterChainMap = new LinkedHashMap<>();
// 配置可以匿名访问的地址,可以根据实际情况自己添加,放行一些静态资源等,anon 表示放行
filterChainMap.put("/css/**", "anon");
filterChainMap.put("/imgs/**", "anon");
filterChainMap.put("/js/**", "anon");
filterChainMap.put("/swagger-*/**", "anon");
filterChainMap.put("/swagger-ui.html/**", "anon");
// 登录 URL 放行
filterChainMap.put("/o2o/login", "anon");
//filterChainMap.put("/o2o/**", "authc");
//配置需要认证的访问地址
filterChainMap.put("/shop/**", "authc");
// 配置 logout 过滤器
filterChainMap.put("/logout", "logout");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager 安全管理器对象
* */
@Bean(name="securityManager")
public DefaultWebSecurityManager
getDefaultWebSercurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
//给安全管理器设置realm(读取用户数据)
securityManager.setRealm(userRealm);
return securityManager;
}
//获取自定义Realm对象,并修改凭证校验匹配器
@Bean(name ="userRealm" )
public UserRealm getRealm(){
UserRealm realm=new UserRealm();
HashedCredentialsMatcher hashedCredentialsMatcher = new
HashedCredentialsMatcher();
// 散列算法:这里使用MD5算法;需要和service中设置加密的值一致
hashedCredentialsMatcher.setHashAlgorithmName("md5");
// 散列的次数,比如散列两次,相当于md5(md5(""));,需要和service中设置加密的值一致
hashedCredentialsMatcher.setHashIterations(5);
//hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
// 表示是否存储散列后的密码为16进制,需要和生成密码时的一样,默认是base64;
realm.setCredentialsMatcher(hashedCredentialsMatcher);
return realm ;
}
*************************************************************************
public class UserRealm extends AuthorizingRealm {
//@Resource看到有人用工具类从applicationContext中获取service层的bean,这里其实可以直接注入
@Autowired
private TbPersonInfoService tbPersonInfoService;
//执行授权逻辑
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection
principalCollection) {
return null;
}
//执行认证逻辑 用户名校验--密码是上一级(父类中自动去完成)
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken
authenticationToken) throws AuthenticationException {
//
// 根据 Token 获取用户名,如果您不知道该 Token 怎么来的,先可以不管,下文会解释
String username = (String) authenticationToken.getPrincipal();
// 根据用户名从数据库中查询该用户
TbPersonInfo user=tbPersonInfoService.getPasswordByUsername(username);
String salt=user.getSalt();
if(user != null) {
// 把当前用户存到 Session 中
SecurityUtils.getSubject().getSession().setAttribute("personInfo", user);
// 传入用户名和密码进行身份认证,并返回认证信息,SimpleAuthenticationInfo构造方法的参数依次为,用户对象(主体),密码名,随机盐,账号名
AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getName(), user.getPassword(),ByteSource.Util.bytes(salt),getName());
return authcInfo;
} else {
return null;
}
}
3.进行登录认证
//登录校验,控制器方法中获取认证主题和Token,并执行登录认证方法
@RequestMapping(value="/tologin",method = RequestMethod.POST)
@ResponseBody
public Map<String,Object> toLogin(@RequestParam(value="loginName") String name,@RequestParam(value="password")String password,HttpServletRequest request){
Map<String,Object> modelmap=new HashMap<>();
// 根据用户名和密码创建 Token
UsernamePasswordToken token = new UsernamePasswordToken(name, password);
// 获取subject认证主体
Subject subject = SecurityUtils.getSubject();
try{
// 开始认证,这一步会跳到我们自定义的 Realm 中
TbPersonInfo user=personInfoService.getPasswordByUsername(name);
subject.login(token);
request.getSession().setAttribute("personInfo", user);
modelmap.put("personInfo",user);
modelmap.put("success",true);
}catch(Exception e){
modelmap.put("success",false);
}
return modelmap;