springboot + shiro + easyui 搭建后台项目
目录
springboot + shiro + easyui 搭建后台项目
(一):shiro配置自定义Realm
realm的概念
Realm是shiro中配置的数据源,用来表明处理用户认证和授权的逻辑,可以配置数据库来进行认证,授权操作,这里我们自定义自己的realm并使用MD5对数据进行加密来进行认证的操作
自定义realm
``
public class CustomRealm extends AuthorizingRealm {
@Autowired
UserService service ;
@Autowired
RoleService roleService ;
@Autowired
PermissionService permissionService ;
@Autowired
MenuService menuService ;
@Autowired
TreeService treeService ;
//授权的方法,当controller需要有权限访问时,就会调用,如果配置了cache就只会调用一次,注意如果配置了缓存那么需要在更改权限的时候,同时清楚掉缓存
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//在认证方法中,如果返回值的第一个参数是用user填充的那么就返回这个user,如果是username,那么就返回username
User user = (User) principalCollection.getPrimaryPrincipal() ;
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo() ;
String username = user.getUsername() ;
//通过用户名查询角色信息
List<Role> roles = roleService.getRolesByUserName(username) ;
for(Role role:roles){
simpleAuthorizationInfo.addRole(role.getRoleName()) ;
//通过角色去获取权限信息
for(Permission permission : permissionService.getPermissionByRoleId(role.getId() )){
simpleAuthorizationInfo.addStringPermission(permission.getPermisson());
}
}
return simpleAuthorizationInfo;
}
//认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取登陆的用户名
String loginName = (String) authenticationToken.getPrincipal();
User user = service.getUserByName(loginName);
//异常处理
if(user == null){
throw new UnknownAccountException() ;
}
if("I".equals(user.getStatus())){
throw new LockedAccountException() ;
}
Session session = SecurityUtils.getSubject().getSession() ;
//将user放入session中
SecurityUtils.getSubject().getSession().setAttribute("user",user );
//把tree 加载到session中
List<Role> roles1 = roleService.getRolesByUserName(user.getUsername()) ;
List<Menu> menuslist = new ArrayList<>() ;
for (Role role:roles1){
List<Menu> menus = menuService.getMenusByRoleId(role.getId()) ;
menuslist.addAll(menus) ;
}
List<EasyuiTreeNode> easyuiTreeNodes = treeService.getTreeNodesByMenus(menuslist) ;
ObjectMapper mapper = new ObjectMapper() ;
String json = null ;
try {
//将list转换成json格式,easyui树需要的数据格式
json = mapper.writeValueAsString(easyuiTreeNodes) ;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
session.setAttribute("tree",json );
System.out.println("-------------" + easyuiTreeNodes);
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),this.getSalt(user.getSalt()),getName() );
return authenticationInfo;
}
private ByteSource getSalt(String salt){
return ByteSource.Util.bytes(salt) ;
}
}
(二):配置MD5加密
在shiro中我们可以配置认证的来源,比如数据库,ini文件等等,这里我们从数据库中进行认证,shiro认证的原理大致是,用户输入用户名和密码,然后通过我们配置的认证处理类(HashedCredentialsMatcher),来判断如何认证,这里我们会配置一个HashedCredentialsMatcher类,设置算法的名字'MD5',hash迭代的次数就是加盐之后在加盐(不知道是不是),设置编码(这个有点坑最好选择true),因为shiro的认证方法中会去判断如果不是true,就会用base64编码,最后可能会造成密码匹配不上。
认证匹配器
``
@Bean
public HashedCredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher() ;
//算法名字,其实可以随便填
credentialsMatcher.setHashAlgorithmName("MD5");
//迭代次数
credentialsMatcher.setHashIterations(2);
//编码的设置
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher ;
}
MD5工具类
这里给大家分享一个MD5工具类(shiro自带的)
``
/**
* shiro工具类
*
* @author dafei, Chill Zhuang
*/
public class ShiroKit {
private static final String NAMES_DELIMETER = ",";
/**
* 加盐参数
*/
public final static String hashAlgorithmName = "MD5";
/**
* 循环次数
*/
public final static int hashIterations = 2;
/**
* shiro密码加密工具类
*
* @param credentials 密码
* @param saltSource 密码盐
* @return
*/
public static String md5(String credentials, String saltSource) {
ByteSource salt = ByteSource.Util.bytes(saltSource);
return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations).toString();
}
/**
* 获取随机盐值
*
* @param
* @return
*/
public static String getRandomSalt(String username) {
String uuid = UUID.randomUUID().toString().replace("-", "") ;
return username + uuid ;
}
}
大家要测试的话可以分享一个列子
``
@RunWith(SpringRunner.class)
@SpringBootTest
public class ShirodemoApplicationTests {
@Test
public void contextLoads() {
String salt = ShiroKit.getRandomSalt( "tom") ;
System.out.println("盐值======" + salt );
String password = ShiroKit.md5("123456",salt ) ;
System.out.println("密码======" + password);
System.out.println("============" + ByteSource.Util.bytes("admin3b41d727b0af4164a9454f6805ac36b9")) ;
}
}
(三):shiroFilter的配置
springboot整合shiro最重要的一部分也就是shirofilter的配置,这列贴一份自己demo的列子,大家可以作为参考,有认为不对的地方,可以指出来
``
@Configuration
public class ShiroConfig{
//对shirofilter进行代理,说句实话 不太懂,没有配也跑起来了 懂得可以说下
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
filterRegistration.setEnabled(true);
filterRegistration.addUrlPatterns("/*");
filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
return filterRegistration;
}
//配置shirofilter,主要配置自定义过滤器链,以及登陆地址,等,注意的地方是,authc配置在最后,不然有可能导致anon的过滤器失效
@Bean("shiroFilter")
public ShiroFilterFactoryBean shirofilter(SecurityManager manager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean() ;
//配置安全管理器
shiroFilterFactoryBean.setSecurityManager(manager);
//登陆controller的mapper 地址
shiroFilterFactoryBean.setLoginUrl("/tologin");
//未认证的地址
shiroFilterFactoryBean.setUnauthorizedUrl("/unauth.jsp");
//登陆后跳转到指定地址
Map<String,Filter> filtersMap = new LinkedHashMap<>() ;
filtersMap.put("authc", new MyFormAuthenticationFilter()) ;
shiroFilterFactoryBean.setFilters(filtersMap);
Map<String,String> map = new LinkedHashMap<>() ;
map.put("unauth.jsp","anon") ;
map.put("/common/**", "anon") ;
map.put("/easyui/**", "anon") ;
map.put("/css/**", "anon");
map.put("/images/**", "anon");
map.put("/hello", "anon") ;
map.put("/login","anon" ) ;
map.put("/logout", "logout") ;
map.put("/**", "authc") ;
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean ;
}
//自定义表单过滤器,主要是为了,在认证成功之后,跳转到指定的url中,因为shiro默认得到是会自动跳转到你之前输入的url中,所以配置表单过滤器,清楚session中的内容,跳转到指定url张
@Bean
public MyFormAuthenticationFilter customFormAuthenticationFilter(){
System.out.println("ShiroConfiguration.formAuthenticationFilter()");
MyFormAuthenticationFilter customFormAuthenticationFilter = new MyFormAuthenticationFilter();
return customFormAuthenticationFilter;
}
//配置表单过滤器时,碰到一个问题,就是这个过滤器和filterchain时同级的,然后他是用authc指定的,所以导致了无法访问anon的url,导致无法导入静态资源
@Bean
public FilterRegistrationBean shiroFilterRegistration(MyFormAuthenticationFilter myFormAuthenticationFilter) {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(myFormAuthenticationFilter);
filterRegistration.setEnabled(false);
return filterRegistration;
}
//配置shiro安全过滤器,用来维护shiro的session管理,以及缓存管理,还有自定义的realm
@Bean
public SecurityManager securityManager(Realm realm,EhCacheManager ehCacheManager,SessionManager sessionManager){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager() ;
defaultWebSecurityManager.setRealm(realm);
defaultWebSecurityManager.setCacheManager(ehCacheManager);
defaultWebSecurityManager.setSessionManager(sessionManager);
return defaultWebSecurityManager ;
}
//自定义realm
@Bean
public CustomRealm customRealm(HashedCredentialsMatcher credentialsMatcher){
CustomRealm customRealm = new CustomRealm() ;
customRealm.setCredentialsMatcher(credentialsMatcher);
customRealm.setCachingEnabled(false); ;
return customRealm ;
}
//认证匹配器
@Bean
public HashedCredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher() ;
credentialsMatcher.setHashAlgorithmName("MD5");
credentialsMatcher.setHashIterations(2);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher ;
}
// 权限配置(注解方式)
/**
* 下面的代码是添加注解支持,支持注解方式的权限配置
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
// 强制使用cglib,防止重复代理和可能引起代理出错的问题
// https://zhuanlan.zhihu.com/p/29161098
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
LifecycleBeanPostProcessor lifecycleBeanPostProcessor = new LifecycleBeanPostProcessor() ;
return lifecycleBeanPostProcessor ;
}
/**
* 开启shiro权限注解功能
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 缓存管理
* @return
*/
@Bean
public EhCacheManager ehCacheManager(){
return new EhCacheManager() ;
}
/**
* session id
* @return
*/
@Bean
public JavaUuidSessionIdGenerator javaUuidSessionIdGenerator(){
return new JavaUuidSessionIdGenerator() ;
}
@Bean
public SessionDAO sessionDAO(JavaUuidSessionIdGenerator javaUuidSessionIdGenerator){
SessionDAO sessionDAO = new EnterpriseCacheSessionDAO() ;
((EnterpriseCacheSessionDAO) sessionDAO).setActiveSessionsCacheName("shiro-activeSessionCache");
((EnterpriseCacheSessionDAO) sessionDAO).setSessionIdGenerator(javaUuidSessionIdGenerator);
return sessionDAO ;
}
@Bean
public SimpleCookie simpleCookie(){
SimpleCookie simpleCookie = new SimpleCookie("mldn-session-id") ;
simpleCookie.setHttpOnly(true);
simpleCookie.setMaxAge(-1);
return simpleCookie ;
}
@Bean
public SessionManager sessionManager(SimpleCookie simpleCookie, SessionDAO sessionDAO, EhCacheManager cacheManager){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager() ;
sessionManager.setSessionIdCookie(simpleCookie);
sessionManager.setSessionDAO(sessionDAO);
sessionManager.setCacheManager(cacheManager);
//session过期时间
sessionManager.setGlobalSessionTimeout(60000*20);
//session验证的时间间隔
sessionManager.setSessionValidationInterval(60000*10);
return sessionManager ;
}
}
(四):自定义表单认证
指定登陆成功之后跳转的地址
``
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
WebUtils.getAndClearSavedRequest(request) ;//清理掉之前session保存的信息
WebUtils.redirectToSavedRequest(request,response ,"/index" );
return false;
}
}
(五):登出处理
这里使用的时默认的登出处理
(六):shiro表的设计
user表
role表
user-role表
permission表
role-permission表
menu表
role-menu表