shiro+spring+springmvc(JavaConfig)

本文介绍了如何将Shiro与Spring、SpringMVC进行简单的整合,通过JavaConfig方式进行配置。内容包括:配置前端控制器、Spring配置、SpringMVC配置、数据库连接配置、Shiro配置、自定义Realm、User实体类、Dao、Service及Controller层的设置。

shiro与spring,springmvc的简单整合(JavaConfig)

(初来乍到,请多包含)
导入核心包,maven会自动导入依赖的包

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.6.0</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.2.6.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.thymeleaf</groupId>
  <artifactId>thymeleaf-spring5</artifactId>
  <version>3.0.11.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.2.6.RELEASE</version>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.47</version>
</dependency>

配置类

配置springmvc的前端控制器

@Configuration
public class MyWebConfig  extends AbstractAnnotationConfigDispatcherServletInitializer {

    /**
     * 增加过滤器
     * @return
     */
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter("utf-8");
//  DelegatingFilterProxy作用是自动到Spring 容器查找名字为shiroFilter(filter-name)的bean并把所有Filter 的操作委托给它。
//     1. 配置  Shiro 的 shiroFilter.
//     2. DelegatingFilterProxy 实际上是 Filter 的一个代理对象. 默认情况下, Spring 会到 IOC 容器中查找和
//      <filter-name> 对应的 filter bean. 也可以通过 targetBeanName 的初始化参数来配置 filter bean 的 id.
        DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy();
        delegatingFilterProxy.setTargetFilterLifecycle(true);
        delegatingFilterProxy.setTargetBeanName("shiroFilterFactoryBean");
        return new Filter[]{encodingFilter,delegatingFilterProxy};
    }

    /**
     * spring的配置类
     * @return
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{RootConfig.class};
    }

    /**
     * springmvc的配置类
     * @return
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{ServletConfig.class};
    }
    /**
    * 拦截的路径
    */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

}

必须配置DelegatingFilterProxy ,否则shiro的filter就会不起作用

spring配置类

@Component
@ComponentScan(basePackages = {"com.cca"},
                            excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class))
//@Import(ShiroConfig.class)
@EnableAspectJAutoProxy
public class RootConfig {

    /**
     * 配置解析${}表达式 作用等于xml中property-placeholder标签
     * @return
     */
    @Bean
    public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
        return new PropertySourcesPlaceholderConfigurer();
    }
}

PropertySourcesPlaceholderConfigurer 是用来解析spel表达式的。

springmvc的配置类

/**
 * Author cca
 * Created by 1398024451@qq.com on 2020/9/28 19:09
 */
@Component
@ComponentScan(basePackages = {"com.cca.controller"})
public class ServletConfig {

}

webmvc的配置

/**
 * Author cca
 * Created by 1398024451@qq.com on 2020/9/28 19:20
 */
@Configuration
public class MyWebMvcConfig extends WebMvcConfigurationSupport {


    @Autowired
    private ServletContext servletContext;

    /**
     * 模板解析器
     * @return
     */
    @Bean
    public ITemplateResolver iTemplateResolver(){
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
        templateResolver.setPrefix("/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setCharacterEncoding("utf-8");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        templateResolver.setCacheable(false);//缓存
        return templateResolver;

    }

    /**
     * 生成模板引擎
     * @param iTemplateResolver
     * @return
     */
    @Bean
    public ISpringTemplateEngine iSpringTemplateEngine(ITemplateResolver iTemplateResolver){
        SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
        springTemplateEngine.setTemplateResolver(iTemplateResolver);
        return springTemplateEngine;
    }

    /**
     * thymeleaf解析器
     * @param iSpringTemplateEngine
     * @return
     */
    @Bean
    public ThymeleafViewResolver resolver(ISpringTemplateEngine iSpringTemplateEngine){
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(iSpringTemplateEngine);
        viewResolver.setCharacterEncoding("utf-8");
        return viewResolver;
    }
    //过滤静态资源
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("/static/");
        registry.addResourceHandler("/index.html").addResourceLocations("/index.html");
    }

    @Override
    protected void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
    }
}

spring的jdbctemplate配置

因为不需要复杂的查询语句,所以博主用了spring的jdbcTemplate来做简单的查询。
@Configuration
@PropertySource(value = "classpath:jdbc.properties")
public class JdbcTemplateConfig {

    @Value("${url}")
    private String url;
    @Value("${diverClass}")
    private String driver;
    @Value("${user}")
    private String user;
    @Value("${pwd}")
    private String pwd;

    @Bean
    public DataSource dataSource(){
        System.out.println(url+"\n"+ user+"\n"+pwd);
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(user);
        dataSource.setPassword(pwd);
        return dataSource;
    }
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
}

shiro配置

/**
 * Author cca
 * Created by 1398024451@qq.com on 2020/9/28 20:13
 */
@Configuration
public class ShiroConfig {
    /**
     * 开启shiro权限注解支持
     * @param webSecurityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager webSecurityManager){
        AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        sourceAdvisor.setSecurityManager(webSecurityManager);
        return sourceAdvisor;
    }

    /**
     * 工厂
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager);

        Map<String ,String> map = new HashMap<>();
        map.put("/index.html", "anon");//放行
        map.put("/user/*","anon");
        map.put("/**", "authc");//拦截

        filterFactoryBean.setLoginUrl("/login");//登录界面
        filterFactoryBean.setUnauthorizedUrl("/user/error");
        filterFactoryBean.setFilterChainDefinitionMap(map);
        return filterFactoryBean;
    }
    @Bean
    public Realm realm(){
        CustomerRealm customerRealm = new CustomerRealm();
        //密码匹配器
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("md5");//加密方式
        matcher.setStoredCredentialsHexEncoded(true);
        matcher.setHashIterations(1024);//散列次数
        customerRealm.setCredentialsMatcher(matcher);
        return customerRealm;
    }

    /**
     * 创建安全管理器
     * @param realm
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager(Realm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }


}

自定义realm配置

/**
 * Author cca
 * Created by 1398024451@qq.com on 2020/9/28 20:16
 */
public class CustomerRealm extends AuthorizingRealm {

    @Autowired //CustomerRealm 通过@bean加入了spring容器,所有能够自动注入
    private UserService userService;

    /**
     * 授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String principal = (String) principalCollection.getPrimaryPrincipal();
        //System.out.println(principal);
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRole("admin1");//增加角色

        return authorizationInfo;
    }

    /**
     * 认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String principal = (String) authenticationToken.getPrincipal();
        String credentials = new String((char[])authenticationToken.getCredentials());
        System.out.println(principal+" "+ credentials);
        //查询用户
        User user = userService.getUserByName(principal);
        if(user != null){
            SimpleAuthenticationInfo authenticationInfo =
                    new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),ByteSource.Util.bytes(user.getSalt()),this.getName());
            return authenticationInfo;
        }
        return null;

    }
}

实体类User

@Setter
@Getter
@ToString
public class User {
    private int id;
    private String username;
    private String password;
    private String salt;
}

这里博主用了lombok插件,如果没有安装插件就生成getset方法

Dao层

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public User getUserByName(String username){
        String sql = "select * from user where username = ?";
        RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class);
        User user = jdbcTemplate.queryForObject(sql, rowMapper,username);

        return user;
    }
}

service层

只是简单的查询语句,所以博主没有定义业务接口。
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public User getUserByName(String username){
        return userDao.getUserByName(username);
    }
}

controller层

@Controller
@RequestMapping("user")
public class MyController {

    @Autowired
    private UserService userService;

//    @ResponseBody
    @RequestMapping("logincheck")
    @Log(log = "登录")
    public String test(String username, String pwd, Model model){
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username,pwd);
        try {
            subject.login(token);
        }catch (Exception e){
            System.out.println("用户名或密码错误");
            model.addAttribute("msg", "用户名或密码错误");
            return "login";
        }
        return "success";
    }

    @ResponseBody
    @RequestMapping("del")
    @Log(log = "删除")
    @RequiresRoles(value = "admin") 
    public String del(String username){
        System.out.println("del.............");
        return "success";
    }

    @RequestMapping("logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "login";
    }


}
使用shiro的权限注解必须要在容器中加入AuthorizationAttributeSourceAdvisor

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

点击删除时会调用contro中的del方法, 改方法上标注了@RequiresRoles(value = "admin") 并设置访问角色位
admin。但是博主在登录授权时给用户授予admin1的权限,可是最后还是跳到了成功页面。这就是容器中没有加
入AuthorizationAttributeSourceAdvisor类。
AuthorizationAttributeSourceAdvisor就相当于一个切入点。

在这里插入图片描述
在这里插入图片描述

AuthorizationAttributeSourceAdvisor加入容器后,注解起作用了。但是却直接爆了异常,可是博主明明在shiro配置类里配置了没有权限的跳转地址,这个却没有有作用。这里就不细说原因了,直接给出解决方案
/**
 * Author cca
 * Created by 1398024451@qq.com on 2020/10/13 14:42
 */
@ControllerAdvice
public class MyException {

    /**
     * shiro无权限异常处理
     * @return
     */
    //@ExceptionHandler(value = UnauthorizedException.class)
    public String unauthorizedException(){
        return "error";
    }
}

直接定义一个全局异常处理类,处理这个异常。

(初来乍到,请多包含)

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值