Apache Shiro,java项目的的安全框架,提供了认证、授权、加密、会话管理等..
Authentication : 身份认证,登录
Authorization: 授权,权限验证
Session Management: 会话管理
Cryptongraphy: 加密
Subject : 主体,代表当前用户,与当前应用交互的任何东西都是Subject,所有subject都绑定到Security Manager
Security Manager : 安全管理器,shiro核心
Realm:域,shiro从realm 获取数据.
基本实现思路 :
1.导入依赖
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
2.继承 AuthorizingRealm,实现自己认证方式MyShiroRealm
/**
* @author heyonghao
* @Title: MyShiroRealm
* @ProjectName xlkb2b
* @Description: 验证,以及权限的添加MyShiroRealm.class
* 实现AuthorizingRealm接口用户用户认证
* @date 2019/3/27 002710:25
*/
public class MyShiroRealm extends AuthorizingRealm {
//这是dao
@Autowired
ShiroLogin shiroLogin;
//角色权限和对应权限添加
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//simpleAuthorizationInfo 用户角色权限管理
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//获取登录用户名
String sysname= (String) principals.getPrimaryPrincipal();
//根据名称查询用户
sysUser user = shiroLogin.findSysUserByName(sysname);
//根据用户-角色码-查询对应-角色
List<sysRole> roles = shiroLogin.findRoleById(user.getRcode());
//添加角色,多角色时,添加list
roles.forEach( r -> simpleAuthorizationInfo.addRole(r.getCaption()));
//添加权限
List<sysPermission> permissions = shiroLogin.findPermissById(user.getPcode());
permissions.forEach(p -> simpleAuthorizationInfo.addStringPermission(p.getCaption()));
return simpleAuthorizationInfo;
}
//用户认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException{
//防止请求未到达时,就开始认证
if (authenticationToken.getPrincipal() == null) {
return null;
}
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
sysUser user = shiroLogin.findSysUserByName(token.getUsername());
//对比用户名
if(StringUtil.objIsEmpty(user)){
return null;
}
//盐
String salt = user.getSalt();
//用户输入-密码
String oringnPassword = new String((char[]) token.getCredentials());
//shiro加密-密码
String encodedPassword = ShiroEncryption.getshiroPasswrod(oringnPassword,salt);
if(!encodedPassword.equals(user.getPasswd())){
throw new IncorrectCredentialsException("密码不正确");
}
return new SimpleAuthenticationInfo(user.getSysname(), oringnPassword, ByteSource.Util.bytes(salt) , getName());
}
}
3.config配置
/**
* @author heyonghao
* @Title: ShiroConfiguration
* @ProjectName xlkb2b
* @Description: 过滤配置
* shiro 基本配置说明:
* - Subject 一个接口定义了很多认证授相关的方法,一个门面,与当前应用交互
* - SecurityManager shiro核心;接口;安全管理器,对全部的subject进行安全管理,继承了Authenticator, Authorizer, SessionManager这三个接口。从Realm获取数据
* - Authenticator 认证器
* - Authorizer 授权器
* - realm 主要复制数据处理;兼部分认证授权校验
* @date 2019/3/27 002710:52
*/
@Configuration
public class ShiroConfiguration {
//将自己的验证方式加入容器
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
return myShiroRealm;
}
//权限管理,配置主要是Realm的管理认证
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
//Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String,String> map = new HashMap<>();
//注意配置顺序
map.put("/static/**", "anon");
map.put("/favicon.ico", "anon");
//登出
map.put("/logout","logout");
//对所有用户认证
map.put("/**","authc");
//登录
shiroFilterFactoryBean.setLoginUrl("/login");
//首页
shiroFilterFactoryBean.setSuccessUrl("/index");
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
//加入注解的使用,不加入这个注解不生效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/***
* shiro的在进行密码验证的时候,将会在此进行匹配
* @return
*/
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("SHA-1");// 散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(3);// 散列的次数,比如散列两次,相当于md5(md5(""));
//hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);// 表示是否存储散列后的密码为16进制,需要和生成密码时的一样,默认是base64;
return hashedCredentialsMatcher;
}
}
4.shiro密码生成配置
public class ShiroEncryption {
/**
* 对用户的密码进行MD5加密
* @param password 密码
* @param salt 盐
* @return
*/
public static String getshiroPasswrod(String password,String salt){
// shiro 自带的工具类生成salt
//String salt = new SecureRandomNumberGenerator().nextBytes().toString();
// 加密次数
int times = 3;
// 算法名称
String algorithmName = "SHA-1";
String encodedPassword = new SimpleHash(algorithmName,password,salt,times).toString();
// 返回加密后的密码
return encodedPassword;
}
}
这里在把mapper贴一下
@Component
@Mapper
public interface ShiroLogin {
/**
* 根据用户名,查询对应管理用户
* @param sysname 用户名
* @return
*/
@Select({" SELECT suid,sysname,passwd,salt,rcode,pcode FROM sys_user WHERE sysname=#{sysname} "})
sysUser findSysUserByName(@Param("sysname") String sysname);
/**
* 根据用户角色码,查询对应角色
* @param codename 角色码
* @return sysRoles 角色集合
*/
@Select({" SELECT uid,caption FROM sys_role WHERE codename=#{codename} "})
List<sysRole> findRoleById(@Param("codename") String codename);
/**
* 根据用户权限码,查询对应权限
* @param codename 权限码
* @return permissions 权限集合
*/
@Select({" SELECT uid,caption FROM sys_permission WHERE codename=#{codename} "})
List<sysPermission> findPermissById(@Param("codename") String codename);
/**
* 管理员用户新增
* @param user 管理员用户
* @return
*/
@Insert({" INSERT INTO sys_user (suid,sysname,passwd,salt,rcode,pcode,usertype) value (#{suid},#{sysname},#{passwd},#{salt},#{rcode},#{pcode},#{usertype}) "})
int saveSysUser(sysUser user);
}
5.登录controller (这里注意,在MyShiroRealm 用户认证的时候,抛出对应异常,这里捕捉!)
//get请求,跳转登陆页
@RequestMapping(value = "/login",method = RequestMethod.GET)
public String login(){
return "shiro_login";
}
//post登录
@RequestMapping(value = "/login",method = RequestMethod.POST)
public String login(@RequestParam("name") String name, @RequestParam("password") String password , Model model){
//添加用户认证信息
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(name,password);
//进行验证,这里可以捕获异常,然后返回对应信息
try {
subject.login(usernamePasswordToken);
} catch (UnknownAccountException e) {
model.addAttribute("message", "用户名错误");
return "shiro_login";
} catch (IncorrectCredentialsException e) {
model.addAttribute("message", "密码错误");
return "shiro_login";
}
return "index";
}
6.用户新增
/**
* 用户添加
* @return json
*/
@RequestMapping(value = "/sysAdd" ,method = RequestMethod.POST)
@ResponseBody
public JSON sysAdd(@RequestBody Map<String,String> paramMap){
// shiro 自带的工具类生成salt
String salt = new SecureRandomNumberGenerator().nextBytes().toString();
String passwd= ShiroEncryption.getshiroPasswrod(paramMap.get("password"),salt);//加密后的密码
return loginService.addUser(paramMap.get("sysname"), passwd, salt, paramMap.get("rcode"), paramMap.get("pcode"),paramMap.get("usertype"));
}
7.注销
//登出
@RequestMapping(value = "/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "forward:/login";
}
特别提示: 在新增第一个用户的时候,可以把 --hashedCredentialsMatcher,密码匹配先注释掉,在数据添加个用户
登录后,就可以正常新增,加密,加盐的 用户了!