天天看点

Spring Security 注解方式开发(基于SSM)

本次踩坑长达4小时...由于感觉以xml配置的方式来配置security不太灵活, 于是开始了踩坑之旅.

一. 引入Maven依赖

<!--
    <spring.version>4.2.4.RELEASE</spring.version>
-->

<!--Spring Security-->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>${spring.version}</version>
</dependency>
           

二. 创建加载用户信息的类,实现UserDetailsService接口

package com.xxxx.xxx.xxx.impl;

import com.usoft.scenery.mapper.AdminRoleMapper;
import com.usoft.scenery.pojo.AdminUser;
import com.usoft.scenery.pojo.Role;
import com.usoft.scenery.pojo.vo.AdminRole;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * 根据用户名从数据库获取对应的管理员信息,从信息中取出角色列表并添加到授权中
 *
 * @author Braycep
 * @date 2019/3/6 12:53
 */

@Service(value = "userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
    @Resource(name = "adminRoleMapper")
    private AdminRoleMapper adminRoleMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        LoggerFactory.getLogger(UserDetailsServiceImpl.class).info("用户:username=" + s + " 请求授权");
        AdminRole adminRole = adminRoleMapper.findOne(s);
        if (adminRole != null) {
            List<AdminUser> adminUser = adminRole.getAdminUsers();
            List<Role> role = adminRole.getRoles();
            if (adminUser != null && adminUser.size() == 1) {
                if (role != null && role.size() > 0) {
                    List<GrantedAuthority> list = new ArrayList<>();
                    for (Role r : role) {
                        list.add(new SimpleGrantedAuthority(r.getMark()));
                    }

                    AdminUser admin = adminUser.get(0);
                    if (admin != null && admin.getState() == 1) {
                        if (admin.getStartTime().getTime() < System.currentTimeMillis() && System.currentTimeMillis() < admin.getEndTime().getTime()) {
                            return new User(s, admin.getPassword(), list);
                        }
                    }
                }
            }
        }
        return null;
    }
}
           

三. 创建配置类

package com.xxx.xxx.xxx.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.annotation.Resource;

/**
 * Security的配置类
 *
 * @author Braycep
 * @date 2019/3/6 14:09
 */
@Configuration
@EnableWebSecurity(debug = false)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    //自定义的身份认证处理类,实现UserDetailsService接口
    @Resource(name = "userDetailsService")
    private UserDetailsService userDetailsService;

    //数据库连接池,用于使用记住我的功能
    @Autowired
    private DruidDataSource dataSource;

    //security自带的密码加密类
    @Bean
    public BCryptPasswordEncoder getBCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //用于对记住我这个功能生成token
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        return tokenRepository;
    }

    //配置访问
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                //放行
                .antMatchers("/fonts/**", "/css/**", "/images/**", "/js/**", "login.html").permitAll()
                //角色 SU 可以访问"/html/**"
                //.antMatchers("/html/**").hasRole("SU")
                //已经认证过的就可以访问
                .anyRequest().authenticated()
                .and()
                .headers()
                //设置页面frame分区的策略
                .frameOptions().sameOrigin()
                .and()
                //使用security的/login登录验证, loginProcessingUrl的值就是表单提交的地址
                .formLogin().loginProcessingUrl("/login").loginPage("/login.html")
                .defaultSuccessUrl("/index.html")
                .failureForwardUrl("/login.html?failed")
                .failureUrl("/login.html?failed")
                .and()
                .logout().logoutUrl("/logout").logoutSuccessUrl("/login.html")
                //记住密码
                .and()
                .rememberMe()
                .tokenRepository(persistentTokenRepository())//设置操作表的Repository
                .tokenValiditySeconds(60 * 60 * 24 * 7)//设置记住我的时间为7天
                .userDetailsService(userDetailsService);//设置userDetailsService

        // 加http.csrf().disable()是为了防止报这个错Whitelabel Error Page
        //This application has no explicit mapping for /error, so you are seeing this as a fallback.
        http.csrf().disable();
    }

    //设置密码加密的方式
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(getBCryptPasswordEncoder());
    }
}
           

四. 注册security配置

package com.xxx.xxx.xxx.config;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;

/**
 * @author Braycep
 * @date 2019/3/6 15:31
 */
public class SecurityWebApplicationInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        //2.1创建AnnotationConfigWebApplicationContext
        AnnotationConfigWebApplicationContext springContext = new AnnotationConfigWebApplicationContext();
        //2.2spring的配置类  springSecurity的配置类
        springContext.register(WebSecurityConfig.class);
    }
}
           

五. 创建初始化类, 必须的

package com.xxx.xxx.xxx.config;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

/**
 * @author Braycep
 * @date 2019/3/6 15:44
 */
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
    //只继承就好
}
           

前端的代码注意表单的提交地址, 应该和loginProcessingUrl中的参数一致, 其余的html只是成功或者失败的跳转页面, loginPage是登录页面, 不是登录提交地址, 并且, 表单只能post提交; /logout这接口一旦请求就会退出登录, 并跳转到loginPage

如果要使用xml配置文件的方式来使用Security, 就需要在web.xml中去加载它, 这里不用在web.xml中加载

记住我功能的数据库表是固定的 

create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null);
           

然后前端的复选框的属性name也要固定为 'remember-me'

<input name="remember-me" type="checkbox" title="记住密码">
           

数据库存储结果:  由Spring Security控制

Spring Security 注解方式开发(基于SSM)

然后部署测试, OK

Spring Security 注解方式开发(基于SSM)