<!--添加日志功能-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!--添加shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!--加入lombok简化项目-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.14</version>
<scope>provided</scope>
</dependency>
package com.xl.xinliao.common.config;
import java.util.*;
import com.xl.xinliao.common.commonEntity.MyShiroRealm;
import com.xl.xinliao.common.listener.ShiroSessionlistener;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpSession;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.shiro.mgt.SecurityManager;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login/");
//没有权限跳转的url
shiroFilterFactoryBean.setUnauthorizedUrl("/login/session");
//拦截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
// 配置不会被拦截的链接 顺序判断
/*定义shiro过滤器,例如实现自定义的FormAuthenticationFilter,需要继承FormAuthenticationFilter
**本例中暂不自定义实现,在下一节实现验证码的例子中体现
*/
/*HashMap<String, Filter> myFilters = new HashMap<>();
myFilters.put("authc", new ShiroControListener());
shiroFilterFactoryBean.setFilters(myFilters);*/
/*定义shiro过滤链 Map结构
* Map中key(xml中是指value值)的第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的
* anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种
* authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
*/
filterChainDefinitionMap.put("/css/**", "anon"); //匿名访问静态资源
filterChainDefinitionMap.put("/font-awesome/**", "anon"); //匿名访问静态资源
filterChainDefinitionMap.put("/fonts/**", "anon"); //匿名访问静态资源
filterChainDefinitionMap.put("/js/**", "anon"); //匿名访问静态资源
filterChainDefinitionMap.put("/lineicons/**", "anon"); //匿名访问静态资源
filterChainDefinitionMap.put("/favicon.con", "anon"); //匿名访问静态资源
filterChainDefinitionMap.put("/img/**", "anon"); //匿名访问静态资源
filterChainDefinitionMap.put("/login/login_sysuser", "anon"); //匿名访问静态资源
// 不需要拦截的访问
//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/login/logout", "logout");
//<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put("/**", "authc");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
System.out.println("ShiroConfiguration.shirFilter()==");
return shiroFilterFactoryBean;
}
/**
* 凭证匹配器
* (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
* )
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(1024);//散列的次数,比如散列两次,相当于 md5(md5(""));
//storedCredentialsHexEncoded默认是true,此时用的是密码加密用的是Hex编码;false时用Base64编码
//hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false);
return hashedCredentialsMatcher;
}
@Bean
public MyShiroRealm myShiroRealm(){
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
myShiroRealm.setCachingEnabled(true);
return myShiroRealm;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
///注入session
securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* 开启shiro aop注解支持.
* 使用代理方式;所以需要开启代码支持;
* @param securityManager
* @return
*/
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
* 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean(name="simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理
mappings.setProperty("UnauthorizedException","403");
r.setExceptionMappings(mappings); // None by default
r.setDefaultErrorView("error"); // No default
r.setExceptionAttribute("ex"); // Default is "exception"
//r.setWarnLogCategory("example.MvcLogger"); // No default
return r;
}
@Bean
public DefaultWebSessionManager sessionManager(){
DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
// Cookie模板,支持Set注入,用户可以自定义模板
Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
// 提升Cookie安全性,防止XSS攻击
cookie.setHttpOnly(true);
Collection<SessionListener> listeners = new ArrayList<SessionListener>();
//配置监听
listeners.add(new ShiroSessionlistener());
sessionManager.setSessionListeners(listeners);
//全局会话超时时间(单位毫秒),默认30分钟 暂时设置为10秒钟 用来测试
sessionManager.setGlobalSessionTimeout(1800000);
//是否开启删除无效的session对象 默认为true
sessionManager.setDeleteInvalidSessions(true);
//是否开启定时调度器进行检测过期session 默认为true
sessionManager.setSessionValidationSchedulerEnabled(true);
//设置session失效的扫描时间, 清理用户直接关闭浏览器造成的孤立会话 默认为 1个小时
//设置该属性 就不需要设置 ExecutorServiceSessionValidationScheduler 底层也是默认自动调用ExecutorServiceSessionValidationScheduler
//暂时设置为 5秒 用来测试
sessionManager.setSessionValidationInterval(3600000);
return sessionManager;
}
}
package com.xl.xinliao.common.commonEntity;
import com.xl.xinliao.system.entity.SysUser;
import com.xl.xinliao.system.service.ISysRoleService;
import com.xl.xinliao.system.service.ISysUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
ISysRoleService iSysRoleService;
@Autowired
ISysUserService iSysUserService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String)principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//获取角色权限
authorizationInfo.setRoles(iSysRoleService.findRoles(username));
//获取用户访问路径的权限
authorizationInfo.setStringPermissions(iSysRoleService.findRolePermissions(username));
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取用户的输入的账号.
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();//(String)token.getPrincipal();
//通过username从数据库中查找 SysUser,如果找到:就验证密码的正确性,没找到:返回空,验证失败
//实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
SysUser userInfo = iSysUserService.selectInfoName(username);
if(userInfo == null){
return null;
}
//System .out.println(userInfo.toString());
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
userInfo, //用户实体类
userInfo.getPassword(),//用户加盐后的密码
ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//用户的盐:login_name+salt
this.getName() //realm name
);
return authenticationInfo;
}
}
/**
* 登录接口
* @param mmap
* @param sysUser
* @return
*/
@RequestMapping("/login_sysuser")//, @PathVariable("id") String id, @RequestParam(value = "name" ,required = false,defaultValue = "0"
public Object index(ModelMap mmap, SysUser sysUser){
if (!StringUtils.isEmpty(sysUser)) {
try {
//shiro密码校验
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(sysUser.getLogin_name(), sysUser.getPassword());//sysUser.getPassword());//sh.toString().toCharArray());
//token.setRememberMe(false);
currentUser.login(token);
//查询用户信息
SysUser login = iLoginService.selectLogininfor(sysUser);
//查询菜单列表
List<SysMenu> menus = iSysMenuService.selectInfoJurisdiction(login.getUser_id());
mmap.put("user", login);
mmap.put("menus", menus);
//加入session
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("userInfo", login);
return "index";
} catch (Exception E) {
//E.printStackTrace();
mmap.put("lp", "用户名或密码错误");
return "login";
}
}
return "login";
}
package com.xl.xinliao.system.entity;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@Data
public class SysUser implements Serializable {
private Long user_id;// bigint(20) 用户ID
private Long dept_id;// bigint(20) 部门ID
private String login_name;// varchar(30) 登录账号
private String user_name ;// varchar(30) 用户昵称
private String user_type;// varchar(2) 用户类型(00系统用户)
private String email;// varchar(50) 用户邮箱
private String phonenumber;// varchar(11) 手机号码
private String sex;// char(1) 用户性别(0男 1女 2未知)
private String avatar;// varchar(100) 头像路径
private String password;// varchar(50) 密码
private String salt;// varchar(20) 盐加密
/**
* 用户盐.
* @return
*/
public String getCredentialsSalt(){
return this.login_name+this.salt;
}
//重新对盐重新进行了定义,用户名+salt,这样就更加不容易被破解
}
实现原理
1.导入相应的pom.xml
2.加入相应的shiro拦截
3.MyShiroRealm 用来登录做校验加密内容
4.然后登录把相应密码传入之后,按照shiro里面的加密规则,与数据库里面加密内容做对比,成功后就会登录成功继续往下走
本节shiro验证我亲测有效。谢谢支持
第一次入住CSDN博客,有什么不懂的可以在下方留言,我看到会对本节内容做详细解决。有什么也可以和我交流