SpringBoot - 整合Shiro

一、引入相关依赖

后面两个依赖可以不引入,还没有使用过Redis来做Shiro的缓存。后续如果有用到,可能会更新到博文。

<!-- Shiro -->
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.4.0</version>
</dependency>
<!-- Shiro-Thymeleaf -->
<dependency>
	<groupId>com.github.theborakompanioni</groupId>
	<artifactId>thymeleaf-extras-shiro</artifactId>
	<version>2.0.0</version>
</dependency>
<!-- Thymeleaf -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- Redis缓存Shiro - 本文未使用,可不引入 -->
<dependency>
	<groupId>org.crazycake</groupId>
	<artifactId>shiro-redis</artifactId>
	<version>2.4.2.1-RELEASE</version>
</dependency>
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-ehcache</artifactId>
	<version>1.4.0</version>
</dependency>

二、基础准备

1、创建用户类(User)
@Data
public class User {
    /** 主键Id */
    private Long id;
    /** 账号 */
    private String username;
    /** 密码 */
    private String password;

    private List<Role> roles;
}
2、创建Service层(UserService)
@Service
public class UserServiceImpl implements UserService {

    @Override
    public User getUserByUsername(String username) {
        User user = new User();
        // 应该从数据库获取,这里写死了
        user.setId(1L);
        user.setUsername("lcy123456");
        user.setPassword("97fe5ea8b72e6a39bd9e500cb462e426");
        return user;
    }

    @Override
    public List<String> getRolesById(Long id) {
        List<String> roles = new ArrayList<>();
        // 应该从数据库获取,这里写死了
        roles.add("hr");
        roles.add("manager");
        return roles;
    }

    @Override
    public List<String> getPermissionById(Long id) {
        List<String> permissions = new ArrayList<>();
        // 应该从数据库获取,这里写死了
        permissions.add("role:index");
        permissions.add("menu:index");
        return permissions;
    }
}
3、静态登录页准备

登录页面
添加按钮在这里的意义是本来想演示:通过这个按钮发起请求,但是没有这个权限的解决办法。(实际没啥用,因为我这里没有权限是通过@ControllerAdvice做了统一处理),只需关注登录即可。

三、自定义AuthorizingRealm

创建一个类继承自AuthorizingRealm,这个类的作用就是用来认证与授权的。

public class UserRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    /**
     * 身份认证
     * 前端form表单通过post请求发送的/login请求
     * 会自动将name为username和password的值放到token里去
     * 当然也可以自己手动提交到token里去 - 本例的做法
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 从Token中获取账户
        String username = (String) authenticationToken.getPrincipal();
        // String password = new String((char[]) authenticationToken.getCredentials());  // 获取密码
        // 直接根据获取到的username去数据库登录用户对象
        User user = userService.getUserByUsername("lcy123456");
        if(user == null){
            throw new AccountException("用户名或密码错误!");
        }
        // 交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配
        // 参数:主体、正确的密码、盐、当前realm名称
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
                user,
                user.getPassword(),
                ByteSource.Util.bytes(user.getUsername()),
                this.getName()
        );
        return info;
    }

    /**
     * 用户授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 获取用户身份信息
        User user = (User) principalCollection.getPrimaryPrincipal();
        // 根据当前角色去查询权限
        List<String> roles = userService.getRolesById(user.getId());
        List<String> permissions = userService.getPermissionById(user.getId());
        // 给授权信息
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(roles);
        info.addStringPermissions(permissions);
        return info;
    }
}

四、Shiro配置文件

@Configuration
public class ShiroConfig {

    /**
     * 配置ShiroDialect,用于Shiro和thymeleaf标签配合使用
     * 可以让Thymealf页面使用shiro标签
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

    /**
     * 将自定义Realm交给Spring管理
     * @return UserRealm
     */
    @Bean
    public UserRealm userRealm(){
        UserRealm userRealm = new UserRealm();
        // 告诉Realm,使用credentialsMatcher加密算法类来验证密文
        userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        // 设置不允许缓存
        userRealm.setCachingEnabled(false);
        // userRealm.setAuthenticationCachingEnabled(true);   // 允许认证缓存
        // userRealm.setAuthenticationCacheName("authenticationCache");
        // userRealm.setAuthorizationCachingEnabled(true);    // 允许授权缓存
        // userRealm.setAuthorizationCacheName("authorizationCache");
        return userRealm;
    }

    /**
     * Shiro核心类:协调Shiro内部的各种安全组件
     * @return SecurityManager
     */
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm
        securityManager.setRealm(userRealm());
//        // 自定义缓存实现 - 使用Redis
//        securityManager.setCacheManager(cacheManager());
//        // 自定义session管理 - 使用redis
//        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    
    /**
     * Shiro过滤器 - 访问权限控制
     * @param securityManager
     * @return
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 设置登录请求url  - 注销的url是/logout
        shiroFilterFactoryBean.setLoginUrl("/login");
//        shiroFilterFactoryBean.setSuccessUrl("/");  // 成功跳转地址
//        // 无权限跳转的请求 - 注解鉴权,这个不会生效
//        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        /* *
         * 过滤链定义
         * 设置访问权限 - Map使用LinkedList,因为它是有顺序的
         * authc:需要验证的url   anno:无需验证的url
         */
        Map<String,String> filterChainMap = new LinkedHashMap<>();
        // 配置某个url需要某个权限码 - 通常用注解的方式
        // filterChainMap.put("/hello", "perms[how_are_you]");
        // 过滤掉静态文件css/js/images - Thymeleaf的静态文件一般放在resources/static下的
        filterChainMap.put("/css/**","anno");
        filterChainMap.put("/js/**","anno");
        filterChainMap.put("/images/**","anno");
        filterChainMap.put("/lib/**","anno");
        // 登录、注册、错误不需要验证
        filterChainMap.put("/login","anno");
        filterChainMap.put("/register","anno");
        filterChainMap.put("/error","anno");
        // 需要拦截验证的url
        filterChainMap.put("/admin/**","authc");
        filterChainMap.put("/user/**","authc");
        // 下面这行拦截所有代码必须在Map最后一个,否则会拦截所有url
        // 这样写的话,就是除了前面无需验证的,剩下的全都得验证
        filterChainMap.put("/**","authc");
        // 注意:认证成功/失败也可以通过shiro的过滤器来做 - FormAuthenticationFilter
        return shiroFilterFactoryBean;
    }

    /**
     * 凭证匹配器 - 匹配密码的规则
     * @return
     */
    @Bean(name = "credentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 使用MD5算法
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 散列此时 - 2次
        hashedCredentialsMatcher.setHashIterations(2);
        // 设置存储的凭证编码,默认为true:即Hex,如果为false,则为Base64编码
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }

    /************************************** 开启注解配置权限start ****************************************/
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 默认的代理创建者
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 授权源
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }
    /************************************** 开启注解配置权限end ****************************************/


    /************************************** 开启Redis缓存start ****************************************/

//    /**
//     * cacheManager 缓存 redis实现
//     * 使用的是shiro-redis开源插件
//     * @return
//     */
//    public RedisCacheManager cacheManager() {
//        RedisCacheManager redisCacheManager = new RedisCacheManager();
//        redisCacheManager.setRedisManager(redisManager());
//        return redisCacheManager;
//    }
//    /**
//     * 配置shiro redisManager
//     * 使用的是shiro-redis开源插件
//     *
//     * @return
//     */
//    @Bean
//    public RedisManager redisManager() {
//        RedisManager redisManager = new RedisManager();
////        redisManager.setHost(host);
////        redisManager.setPort(port);
////        // 配置缓存过期时间
////        redisManager.setExpire(expireTime);
////        redisManager.setTimeout(timeOut);
////         redisManager.setPassword(password);
//        return redisManager;
//    }
//
//    /**
//     * Session Manager
//     * 使用的是shiro-redis开源插件
//     */
//    @Bean
//    public DefaultWebSessionManager sessionManager() {
//        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
//        sessionManager.setSessionDAO(redisSessionDAO());
//        return sessionManager;
//    }
//
//    /**
//     * RedisSessionDAO shiro sessionDao层的实现 通过redis
//     * 使用的是shiro-redis开源插件
//     */
//    @Bean
//    public RedisSessionDAO redisSessionDAO() {
//        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
//        redisSessionDAO.setRedisManager(redisManager());
//        return redisSessionDAO;
//    }
    /************************************** 开启Redis缓存end ****************************************/
}

因为做了密码加密,所以注册的时候也需要对密码加密,如下所示:

@RunWith(SpringRunner.class)
@SpringBootTest
public class Test1 {
    @Test
    public void test(){
    	// MD5算法,对123456进行加盐并进行2次散列
        String md5Pwd = new SimpleHash("MD5", "123456",
                ByteSource.Util.bytes("lcy123456"), 2).toHex();
        System.out.println(md5Pwd);
    }
}

五、Thymeleaf页面使用Shiro标签

要想在thymeleaf使用shiro的标签,需要引入2.0以上的thymeleaf-extras-shiro依赖

<!DOCTYPE html>
<!-- 需加入对应的命名空间 -->
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="/logout">注销</a>
<h1 shiro:hasPermission="role:index">role:index</h1>
<h1 shiro:hasPermission="role:indexAbc">role:indexAbc</h1>
<h1 shiro:hasRole="manager">manager</h1>
<h1 shiro:hasRole="abc">abc</h1>
</body>
</html>

六、Controller的书写

Controller层的书写主要是演示手动提交token和手动注销,以及权限注解RequiresPermissions的使用。

@Controller
public class ReController {
    /**
     * 返回JSON数据 - 跳转交给前端来做,这里只是示例
     * 但是似乎前端没有办法跳转到templates下的页面
     * 根据业务做,可以不返回Json数据,直接进行跳转即可
     * 这里因为前端使用的是axios/ajax请求,自己手动提交token
     * @param username 账号
     * @param password 密码
     * @return
     */
    @PostMapping("/login")
    @ResponseBody
    public String Login(@RequestParam("username") String username, @RequestParam("password") String password){
        // 从SecurityUtils里边创建一个 subject
        Subject subject = SecurityUtils.getSubject();
        // 在认证提交前准备 token(令牌)
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        // 执行认证登陆
        try {
            subject.login(token);
        } catch (UnknownAccountException uae) {
            return "未知账户";
        } catch (IncorrectCredentialsException ice) {
            return "密码不正确";
        } catch (LockedAccountException lae) {
            return "账户已锁定";
        } catch (ExcessiveAttemptsException eae) {
            return "用户名或密码错误次数过多";
        } catch (AuthenticationException ae) {
            return "用户名或密码不正确!";
        }
        if (subject.isAuthenticated()) {
            return "登录成功";
        } else {
            token.clear();
            return "登录失败";
        }
    }

    /**
     * 这个就是针对上面那个返回JSON的反面例子
     * 直接跳转到templates下的loginhtml.html
     * 这里只是给大家两种思路:可以直接返回页面,也可以让前端来跳转
     * @return
     */
    @GetMapping("/logout")
    public String logout(){
        Subject lvSubject=SecurityUtils.getSubject();
        // 注销
        lvSubject.logout();
        return "loginhtml";
    }

    /**
     * templates下的/loginhtml.html页面(登录页面)
     * @return
     */
    @GetMapping("/")
    public String index(){
        return "loginhtml";
    }


    @GetMapping("/index")
    public String indexTo(){
        return "index";
    }


    @GetMapping("/admin/add")
    @RequiresPermissions("user:list")
    public String add(){
        return "新增成功!";
    }

    @GetMapping("/role/index")
    @RequiresPermissions("role:index")
    public String addRole(){
        return "新增角色成功!";
    }

}

七、无权限异常处理

@ControllerAdvice
public class ControllerExceptionHandler {
    @ExceptionHandler(AuthorizationException.class)
    public ModelAndView exceptionHandler(HttpServletRequest request, Exception e){
        ModelAndView mv = new ModelAndView();
        mv.setViewName("/403");
        return mv;
    }
}

以上代码总结:

基本演示了前后端分离和非前后端分离的解决方案(不全),对于无权访问也给出了解决方案,演示了Shiro标签在Thymealf页面上的使用。不足的是,因为没有具体的项目做支撑,因此很多地方想的不周全。这里推荐一篇博文:SpringBoot项目+Shiro(权限框架)+Redis(缓存)集成
最后就是:文中所有的return跳转页面,都是跳转的templates下的页面。而使用ajax请求的则是通过前端进行跳转的,则是在static下的页面。

补充:Shiro标签

Shiro标签原文

guest标签
  <shiro:guest>
  </shiro:guest>
  用户没有身份验证时显示相应信息,即游客访问信息。

user标签
  <shiro:user>  
  </shiro:user>
  用户已经身份验证/记住我登录后显示相应的信息。

authenticated标签
  <shiro:authenticated>  
  </shiro:authenticated>
  用户已经身份验证通过,即Subject.login登录成功,不是记住我登录的。

notAuthenticated标签
  <shiro:notAuthenticated>
  
  </shiro:notAuthenticated>
  用户已经身份验证通过,即没有调用Subject.login进行登录,包括记住我
    自动登录的也属于未进行身份验证。

principal标签
  <shiro: principal/>
  
  <shiro:principal property="username"/>
  相当于((User)Subject.getPrincipals()).getUsername()。

lacksPermission标签
  <shiro:lacksPermission name="org:create">
 
  </shiro:lacksPermission>
  如果当前Subject没有权限将显示body体内容。

hasRole标签
  <shiro:hasRole name="admin">  
  </shiro:hasRole>
  如果当前Subject有角色将显示body体内容。

hasAnyRoles标签
  <shiro:hasAnyRoles name="admin,user">
   
  </shiro:hasAnyRoles>
  如果当前Subject有任意一个角色(或的关系)将显示body体内容。

lacksRole标签
  <shiro:lacksRole name="abc">  
  </shiro:lacksRole>
  如果当前Subject没有角色将显示body体内容。

hasPermission标签
  <shiro:hasPermission name="user:create">  
  </shiro:hasPermission>
  如果当前Subject有权限将显示body体内容
发布了95 篇原创文章 · 获赞 23 · 访问量 1万+
展开阅读全文

SpringBoot集成shiro-redis遇到的问题(已解决)

08-24

在通过***(SysUser) SecurityUtils.getSubject().getPrincipal()*** 获取当前登录对象时,**SecurityUtils.getSubject().getPrincipal()**是正确的对象。 当强转时报错com.spring.model.system.SysUser cannot be cast to com.spring.model.system.SysUser。 求解本人猜测是redis对象序列化反序列化导致,因为shiro没有集成redis时是正确的,但是经过测试,不走shiro单独存储读取对象是没有问题的,这块不是太懂,求大神解答 下面是相关的配置代码 redis配置 ``` @Configuration @EnableCaching @EnableRedisHttpSession public class RedisConfig extends CachingConfigurerSupport { @Bean public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) { RedisCacheManager manager = new RedisCacheManager(redisTemplate); manager.setDefaultExpiration(3600);//设置默认过期时间 return manager; } @Bean public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) { StringRedisTemplate template = new StringRedisTemplate(factory); Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } @Bean public KeyGenerator keyGenerator() { return new KeyGenerator() { @Override public Object generate(Object target, Method method, Object... params) { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append(method.getName()); for (Object obj : params) { sb.append(obj.toString()); } return sb.toString(); } }; } } ``` ``` shiro配置 @Configuration public class ShiroConfig { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.timeout}") private int timeout; @Value("${spring.redis.password}") private String password; @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); //拦截器. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // 配置不会被拦截的链接 顺序判断 filterChainDefinitionMap.put("/doLogin", "anon"); filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/images/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/libs/**", "anon"); // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/login"); //未授权界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 配置需要验证登录后访问的链接 filterChainDefinitionMap.put("/**", "authc"); // 从数据库获取 // List<AdminMenu> list = systemService.selectAllMenu(); // // for (AdminMenu menu : list) { // filterChainDefinitionMap.put(menu.getMenuUrl(), "authc"); // } shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 凭证匹配器 * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了) * * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法; hashedCredentialsMatcher.setHashIterations(1);//散列的次数 return hashedCredentialsMatcher; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm()); // 自定义缓存实现 使用redis securityManager.setCacheManager(cacheManager()); // 自定义session管理 使用redis securityManager.setSessionManager(sessionManager()); //注入记住我管理器; securityManager.setRememberMeManager(rememberMeManager()); return securityManager; } /** * 身份认证realm; (自定义,账号密码校验;权限等) * * @return */ @Bean public MyShiroRealm myShiroRealm() { MyShiroRealm myShiroRealm = new MyShiroRealm(); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return myShiroRealm; } /** * 配置shiro redisManager * <p> * 使用的是shiro-redis开源插件 * * @return */ public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost(host); redisManager.setPort(port); redisManager.setExpire(1800);// 配置缓存过期时间 redisManager.setTimeout(timeout); redisManager.setPassword(password); return redisManager; } /** * cacheManager 缓存 redis实现 * <p> * 使用的是shiro-redis开源插件 * * @return */ @Bean public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); return redisCacheManager; } /** * RedisSessionDAO shiro sessionDao层的实现 通过redis * <p> * 使用的是shiro-redis开源插件 */ @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); return redisSessionDAO; } /** * Session Manager * <p> * 使用的是shiro-redis开源插件 */ @Bean public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionDAO(redisSessionDAO()); return sessionManager; } /** * cookie对象; * * @return */ public SimpleCookie rememberMeCookie() { //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); //<!-- 记住我cookie生效时间7天 ,单位秒;--> simpleCookie.setMaxAge(604800); return simpleCookie; } /** * cookie管理对象;记住我功能 * * @return */ @Bean public CookieRememberMeManager rememberMeManager() { CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位) cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag==")); return cookieRememberMeManager; } /** * 开启shiro aop注解支持. * 使用代理方式;所以需要开启代码支持; * * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } } ``` # 去除spring-boot-devtools热部署jar包即可,具体原因不详 问答

springboot+thymeleaf模版引入静态资源不对问题

11-18

我写了一个方法LoginController.java没有任何逻辑直接跳转页面,然后页面样式、js、图片都没有引用进来;下面上下图 先说下问题 页面使用了th标签引入了css/js/图片这些静态资源, 自地址栏直接访问没有问题可以看到 但是我访问http://localhost:8080/users/login(通过方法跳转到页面)出现如下问题 在请求静态资源的时候默认的给我加上了/users,导致资源请求不到,请各位大侠帮忙看看是什么原因, 目录结构: controller代码: package com.dfyz.controller.admin; import com.dfyz.Message; import com.dfyz.entity.Member; import com.dfyz.service.MemberService; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; /** * Created by Administrator on 2017/11/16. */ @Controller @RequestMapping("/users") public class LoginController { @Resource(name = "memberService") private MemberService memberService; @RequestMapping("/login") public String login(String userName, String password, ModelMap map){ Member member = memberService.find(1L); // if(member.getPassword().equals(password.trim())){ // return Message.success("成功"); // } // return Message.error("请核对用户名和密码"); map.addAttribute("member",member); return "admin/index"; } } 页面代码:(将body中的内容删掉了) ``` <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <title>xxx专用</title> <meta name="description" content="估计只有你一个人用了"/> <meta name="keywords" content="index"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="renderer" content="webkit"/> <meta http-equiv="Cache-Control" content="no-siteapp" /> <link rel="icon" type="image/png" th:href="@{assets/i/favicon-bak.png}" /> <link rel="apple-touch-icon-precomposed" th:href="@{assets/i/app-icon72x72@2x.png}" /> <meta name="apple-mobile-web-app-title" content="Amaze UI" /> <script th:src="@{assets/js/echarts.min.js}"></script> <link rel="stylesheet" th:href="@{assets/css/amazeui.min.css}" /> <link rel="stylesheet" th:href="@{assets/css/amazeui.datatables.min.css}" /> <link rel="stylesheet" th:href="@{assets/css/app.css}" /> <script th:src="@{assets/js/jquery.min.js}"></script> </head> <body data-type="index"> </body> </html> ``` 配置文件: ``` # 端口 #server.port=80 # 自动扫描的包前缀 entitymanager.packagesToScan= com.dfyz ## 自动扫描的包前缀 spring.datasource.url = jdbc:mysql://localhost:3306/test spring.datasource.username = root spring.datasource.password = root spring.datasource.driverClassName = com.mysql.jdbc.Driver spring.datasource.maxActive=500 # Specify the DBMS spring.jpa.database = MYSQL # Show or not log for each sql query spring.jpa.show-sql = true # Hibernate ddl auto (create, create-drop, update) spring.jpa.properties.hibernate.hbm2ddl.auto=update # spring.jpa.hibernate.ddl-auto = update # Naming strategy spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy # stripped before adding them to the entity manager) spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext ######################################################## ###THYMELEAF (ThymeleafAutoConfiguration) ######################################################## #spring.thymeleaf.prefix=classpath:/templates/ #spring.thymeleaf.suffix=.html #spring.thymeleaf.mode=HTML5 #spring.thymeleaf.encoding=UTF-8 # ;charset=<encoding> is added #spring.thymeleaf.content-type=text/html # set to false for hot refresh spring.thymeleaf.mode=LEGACYHTML5 spring.thymeleaf.cache=false # 静态文件请求匹配方式 spring.mvc.static-path-pattern=/** # 修改默认的静态寻址资源目录 spring.resources.static-locations = classpath:/templates/,classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/ #热部署生效 spring.devtools.restart.enabled=true cbs.imagesPath=file:/E:/imagesuuuu/ ``` 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 游动-白 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览