美文网首页
spring mvc集成spring security案例

spring mvc集成spring security案例

作者: 千叶鸟 | 来源:发表于2017-03-07 19:44 被阅读641次

    文章首发 http://surpass.esy.es

    代码结构

    总共涉及到7个类文件和添加角色配置文件以及需要在web.xml中添加一个过滤器(如果是spring boot的话则不需要配置web.xml)

    代码结构

    涉及文件及其作用简单分析

    1. role-config.properties:用来配置所有角色及所持权限信息
    2. Role.java:将配置文件中的权限信息分装到java类中,方便处理
    3. RoleHandler.java:构建Role对象列表,以便可以通过配置文件的key获取到相关Role对象
    4. 'UserDtailsBean.java':根绝具体业务需求来给用户对象添加(自定义)security需要的属性
    5. 'UserDetailsServiceCustom':自定义用户信息获取服务,主要是重写loadUserByUsername方法
    6. AuthenticationProviderCustom.java:自定义授权认证类,当用户登录,则会调用该方法,授权操作就是在这里进行的
    7. CustomSuccessHandler:当登录成功后的处理操作,可以省去或直接跳转到某个页面
    8. SecurityConfig:Security的核心配置类,在这里使用了自定义的AuthenticationProviderCustom和AuthenticationProviderCustom进行用户信息的构建以及授权类,具体配置内容可参考官网(当然,也可以用XML进行配置,而不是Java Config的形式)
    9. web.xml:配置security过滤器(使用spring boot的话无须配置)

    具体代码案例

    1.在配置文件中配置好各个角色的信息,security会默认解析以ROLE_开头的权限内容作为角色,例如ROLE_ADMIN,则将作为ADMIN角色

    角色配置

    2.添加角色Bean对象

    import java.util.List;
    
    public class Role {
        private String name;    //  名称
        private List<String> privileges;    //  分配的角色/权限
    
        Role(){
    
        }
    
        Role(String name, List<String> privileges){
            this.name = name;
            this.privileges = privileges;
        }
    
        List<String> getPrivileges(){
            return this.privileges;
        }
    
        String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    

    3.提供角色权限初始化类

    import com.x.x.common.UserType;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 角色权限管理以及初始化
     * 目前是读取配置文件来初始化用户权限
     * <p>
     * Created by surpass.wei@gmail.com on 2017/3/6.
     */
    @Component
    public class RoleHandler {
    
        @Value("${role.admin}")
        private String adminRoleStr;
    
        @Value("${role.teacher}")
        private String teacherRoleStr;
    
        @Value("${role.trainee}")
        private String traineeRoleStr;
    
        private final List<Role> roleList = new ArrayList<>();
    
        /**
         * 根据config.properties配置文件中的角色权限控制来初始化roles
         */
        private void init() {
            UserType[] userTypes = UserType.values();
    
            Role role;
            List<String> privilegeList;
            /**
             * 循环角色类型获取相应的配置字符串,根据“,”分隔
             * 构建Role的List
             */
            for (UserType userType : userTypes) {
    
                String currentRoleStr;  //  当前角色的权限信息
                if (userType.equals(UserType.ADMIN)) {
                    currentRoleStr = adminRoleStr;
                } else if (userType.equals(UserType.TEACHER)) {
                    currentRoleStr = teacherRoleStr;
                } else if (userType.equals(UserType.TRAINEE)) {
                    currentRoleStr = traineeRoleStr;
                } else {
                    continue;
                }
                if (currentRoleStr.isEmpty()) {
                    return;
                }
    
                privilegeList = new ArrayList<>();
                String[] roleArr = currentRoleStr.split(",");
                for (String s : roleArr) {
                    privilegeList.add(s.trim());
                }
                role = new Role(userType.getRoleName(), privilegeList);
    
                roleList.add(role);
            }
    
        }
    
        public Role getRole(String roleName) {
            if (roleList.isEmpty()) {
                this.init();
            }
    
            if (roleName == null || roleName.isEmpty()) {
                return null;
            }
    
            Role role = new Role();
            for (Role temp_role : roleList) {
                if (temp_role.getName().compareToIgnoreCase(roleName) == 0) {
                    role = temp_role;
                }
            }
    
            return role;
        }
    }
    

    4.封装UserDetailBean

    import com.x.x.common.UserType;
    import com.x.x.entity.User;
    import com.x.x.util.BeanUtil;
    import com.x.x.util.SpringContextUtil;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    
    import java.util.ArrayList;
    import java.util.Collection;
    
    /**
     * 基于User包装一个UserDetailsBean(用于security权限认证)
     * 主要为了实现 getAuthorities()
     */
    public class UserDetailsBean extends User implements UserDetails {
    
        public UserDetailsBean(User user) {
            BeanUtil.copyObjValue(user, this);
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            //  获取用户的角色/权限信息
    
            Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
            RoleHandler roleHandler = SpringContextUtil.getApplicationContext().getBean(RoleHandler.class);
    
            if (roleHandler == null) {
                return null;
            }
    
            // 根据用户的类型, 构建用户对应的Role
            String roleName;
    
            roleName = UserType.getRoleName(this.getUserType());
            Role role = roleHandler.getRole(roleName);
    
            //  循环角色的权限信息, 构建authorities
            for (String privilege : role.getPrivileges()) {
                authorities.add(new SimpleGrantedAuthority(privilege));
            }
    
            return authorities;
        }
    
        @Override
        public String getPassword() {
            return this.getPwd();
        }
    
        @Override
        public String getUsername() {
            return this.getLoginName();
        }
    
        @Override
        public boolean isAccountNonExpired() {
            //  账户是否未过期
            return true;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            //  账户是否未锁定
            return this.getUserState() == 0;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            // 凭证是否未过期
            return true;
        }
    
        @Override
        public boolean isEnabled() {
            //  是否有效
            return true;
        }
    }
    

    5.实现UserDetailsService重写loadUserByUsername方法

    import com.x.x.entity.User;
    import com.x.x.service.IUserService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    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.Component;
    
    import javax.annotation.Resource;
    
    /**
     * 重写loadUserByUsername
     * Created by surpass.wei@gmail.com on 2016/12/16.
     */
    @Component
    public class UserDetailsServiceCustom implements UserDetailsService {
        private Logger logger = LoggerFactory.getLogger(UserDetailsServiceCustom.class);
    
        @Resource
        private IUserService userService;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
            UserDetailsBean userDetailsBean;
    
            User search = new User();
            search.setLoginName(username);
            User user = userService.findOneByEntity(search);
    
            if (user==null) {
                logger.warn("该用户不存在:" + username);
                throw new UsernameNotFoundException("该用户不存在:" + username);
            } else {
                userDetailsBean = new UserDetailsBean(user);
            }
    
            return userDetailsBean;
        }
    }
    

    6.自定义的授权认证类

    import com.x.x.entity.User;
    import com.x.x.service.IUserService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    
    @Component
    public class AuthenticationProviderCustom implements AuthenticationProvider {
        private Logger logger = LoggerFactory.getLogger(AuthenticationProviderCustom.class);
    
        @Resource
        private IUserService userService;
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
            String loginName = token.getName();
            String password = token.getCredentials().toString();
            User user = userService.getUserByLoginNameAndPwd(loginName, password);
    
            UserDetailsBean userDetailsBean;
            if(user != null) {
                userDetailsBean = new UserDetailsBean(user);
            } else {
                logger.error("用户名或密码错误");
                throw new BadCredentialsException("用户名或密码错误");
            }
    
            return new UsernamePasswordAuthenticationToken(userDetailsBean, password, userDetailsBean.getAuthorities());
        }
    
        @Override
        public boolean supports(Class<?> aClass) {
            return true;
        }
    }
    

    7.配置Security

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    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 javax.annotation.Resource;
    
    
    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    @EnableWebSecurity //这里如果放开js文件将不被允许执行,注释掉也没有影响,不知道为什么
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Resource
        private CustomSuccessHandler customSuccessHandler;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/**").permitAll()
                    //.antMatchers("/static/**").permitAll()  //  允许所有用户对根路径以及匹配"/static/"开头的路径的访问
                    //.antMatchers("/", "/index").hasRole("TEACHER")
                    .anyRequest().authenticated()   //  任何尚未匹配的的URL地址只需要对用户进行权限验证
                    .and()
                    .formLogin()
                        .successHandler(customSuccessHandler)
                        .failureUrl("/login?error=true")
                        //.defaultSuccessUrl("/home")
                        //.defaultSuccessUrl("/swagger-ui.html")  //  登陆成功后默认默认跳转到swagger页
                        .permitAll()
                        .and()
                        .rememberMe().tokenValiditySeconds(604800);
            http.logout();
            http.csrf().disable();
        }
    
        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            //  将验证过程交给自定义的授权认证类
            auth.authenticationProvider(authenticationProvider());
        }
    
        @Bean
        @Override
        protected UserDetailsService userDetailsService() {
            return new UserDetailsServiceCustom();
        }
    
        @Bean
        public AuthenticationProvider authenticationProvider() {
            return new AuthenticationProviderCustom();
        }
    }
    

    8.自定义成功处理类(可选)

    import org.springframework.security.core.Authentication;
    import org.springframework.security.web.DefaultRedirectStrategy;
    import org.springframework.security.web.RedirectStrategy;
    import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    
    @Component
    public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
        private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    
        @Override
        protected void handle(HttpServletRequest request, HttpServletResponse response,
                              Authentication authentication) throws IOException, ServletException {
            UserDetailsBean user = (UserDetailsBean) authentication.getPrincipal();
    
            String targetUrl;
    
            if (UserType.ADMIN.getValue().equals(user.getUserType())) {
                targetUrl = "/swagger-ui.html";
            } else if (UserType.TEACHER.getValue().equals(user.getUserType())) {
                targetUrl = "/teacher";
            } else if (UserType.TRAINEE.getValue().equals(user.getUserType())){
                targetUrl = "/trainee";
            } else {
                targetUrl = "/login?error";
            }
    
            redirectStrategy.sendRedirect(request, response, targetUrl);
        }
    }
    

    9.添加security过滤器

      <!--spring security-->
      <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
      </filter>
    
      <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
      </filter-mapping>
    

    相关文章

      网友评论

          本文标题:spring mvc集成spring security案例

          本文链接:https://www.haomeiwen.com/subject/qnfvgttx.html