07 Spring Security 之 WebSecurityConfig

Spring Security介紹

大部分系統(tǒng)荆萤,都需要認證和鑒權的功能。SpringBoot常用的安全框架spring security和shiro箕般。

shiro相對來說簡單易用,spring security功能更完善一點谦疾。

本文介紹spring security的集成方法纷闺,以及使用數(shù)據(jù)庫維護權限數(shù)據(jù),包括用戶凳谦、權限

使用數(shù)據(jù)庫維護用戶數(shù)據(jù)

數(shù)據(jù)庫表設計

這里簡化一下忆畅,直接實現(xiàn)User和Role的映射,而省略了Role和資源的映射尸执。

CREATE TABLE `Users` (
  `UserId` int(11) NOT NULL AUTO_INCREMENT,
  `UserName` varchar(45) NOT NULL,
  `PassWord` varchar(100) NOT NULL,
  `LockedFlag` tinyint(4) NOT NULL,
  PRIMARY KEY (`UserId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `UserRole` (
  `UserRoleId` int(11) NOT NULL AUTO_INCREMENT,
  `UserId` int(11) NOT NULL,
  `RoleId` int(11) NOT NULL,
  PRIMARY KEY (`UserRoleId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Role` (
  `RoleId` int(11) NOT NULL AUTO_INCREMENT,
  `RoleCode` varchar(45) NOT NULL,
  `RoleDesc` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`RoleId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Mybatis代碼

如果你看過前幾章mybatis方面的內容家凯,可以直接自己實現(xiàn)一下,跳過Mybatis這部分內容如失。

bean層

//RoleBean
package com.it_laowu.springbootstudy.springbootstudydemo.bean;

import java.io.Serializable;
import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
@SuppressWarnings("serial")
public class RoleBean implements Serializable {
    private int  roleId;
    private String roleCode;
    private String roleDesc;
}
//UserBean
......省略package和import
@Data
@Accessors(chain = true)
@SuppressWarnings("serial")
public class UserBean implements Serializable {
    private int  userId;
    private String userName;
    private String passWord;
    private String lockedFlag;
}
//UserRoleBean
......省略package和import
@Data
@Accessors(chain = true)
@SuppressWarnings("serial")
public class UserRoleBean implements Serializable {
    private int  userRoleId;
    private int userId;
    private int roleId;
}

Condition層

//RoleCondition
package com.it_laowu.springbootstudy.springbootstudydemo.bean.condition;

import com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean;
import com.it_laowu.springbootstudy.springbootstudydemo.core.base.BaseCondition;
import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class RoleCondition extends BaseCondition {
    private int  roleId;
    private String roleCode;
    private String roleDesc;

    @Override
    public Class<?> getChildClass() {
        return RoleBean.class;
    }
}
//UserCondition
......省略package和import
@Data
@Accessors(chain = true)
public class UserCondition extends BaseCondition {
    private int  userId;
    private String userName;
    private String passWord;
    private String lockedFlag;

    @Override
    public Class<?> getChildClass() {
        return UserBean.class;
    }
}
//UserRoleCondition
......省略package和import
@Data
@Accessors(chain = true)
public class UserRoleCondition extends BaseCondition {
    private int  userRoleId;
    private int userId;
    private int roleId;

    @Override
    public Class<?> getChildClass() {
        return UserRoleBean.class;
    }
}

dao層

//RoleDao
package com.it_laowu.springbootstudy.springbootstudydemo.dao;

import java.util.List;

import com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.condition.RoleCondition;
import com.it_laowu.springbootstudy.springbootstudydemo.core.base.IBaseDao;

import org.apache.ibatis.annotations.Param;


public interface RoleDao extends IBaseDao<RoleBean,RoleCondition> {
    List<RoleBean> getUserRolesByUserId(@Param("keyId") int userId);
}
//UserDao
......省略package和import
public interface UserDao extends IBaseDao<UserBean,UserCondition> {
    UserBean findByName(@Param("username") String username);
}
//UserRoleDao
......省略package和import
public interface UserRoleDao extends IBaseDao<UserRoleBean,UserRoleCondition> {
}

service層

//IRoleService
package com.it_laowu.springbootstudy.springbootstudydemo.service;

import com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.condition.RoleCondition;
import com.it_laowu.springbootstudy.springbootstudydemo.core.base.IBaseService;

public interface IRoleService extends IBaseService<RoleBean,RoleCondition>  {
    List<RoleBean> getUserRolesByUserId(@Param("keyId") int userId);
}
//IUserRoleService
......省略package和import
public interface IUserRoleService extends IBaseService<UserRoleBean,UserRoleCondition>  {
}
//IUserService
......省略package和import
public interface IUserService extends IBaseService<UserBean,UserCondition>  {
    UserBean findByName(@Param("username") String username);
}

ServiceImpl層

//RoleServiceImpl
package com.it_laowu.springbootstudy.springbootstudydemo.service.impl;

import java.util.List;

import com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.condition.RoleCondition;
import com.it_laowu.springbootstudy.springbootstudydemo.core.base.IBaseDao;
import com.it_laowu.springbootstudy.springbootstudydemo.dao.RoleDao;
import com.it_laowu.springbootstudy.springbootstudydemo.service.IRoleService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class RoleServiceImpl implements IRoleService {
    @Autowired
    public RoleDao roleDao;

    @Override
    public IBaseDao<RoleBean, RoleCondition> getBaseDao() {
        return roleDao;
    }
    @Override
    public List<RoleBean> getUserRolesByUserId(int userId) {
        return roleDao.getUserRolesByUserId(userId);
    }
}
//UserRoleServiceImpl
......省略package和import
@Service
public class UserRoleServiceImpl implements IUserRoleService {
    @Override
    public IBaseDao<UserRoleBean, UserRoleCondition> getBaseDao() {
        return userRoleDao;
    }
}
//UserServiceImpl
......省略package和import
@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    public UserDao userDao;

    @Override
    public IBaseDao<UserBean, UserCondition> getBaseDao() {
        return userDao;
    }
    @Override
    public UserBean findByName(String username) {
        return userDao.findByName(username);
    }
}

mybatis.xml

role.mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.it_laowu.springbootstudy.springbootstudydemo.dao.RoleDao">
    <resultMap id="RoleResultMap" type="com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean">
        <result column="roleId" property="roleId"/>
        <result column="roleCode" property="roleCode"/>
        <result column="roleDesc" property="roleDesc"/>
    </resultMap>

    <select id="findAll" resultMap="RoleResultMap">
        select roleId,roleCode,`roleDesc`
        from `Role`
        <where>
            <if test="conditionQC.roleId != 0">
                and roleId = #{conditionQC.roleId}
            </if>
            <if test="conditionQC.roleCode != null and '' != conditionQC.roleCode">
                and roleCode like concat('%',#{conditionQC.roleCode},'%')
            </if>
            <if test="conditionQC.roleDesc != null and '' != conditionQC.roleDesc">
                and roleDesc like concat('%',#{conditionQC.roleDesc},'%')
            </if>
        </where>
        <choose>
            <when test="conditionQC.sortSql == null">
                Order by roleId
            </when>
            <otherwise>
                ${conditionQC.sortSql}
            </otherwise>
        </choose>
    </select>

    <select id="findOne" resultMap="RoleResultMap">
        select roleId,roleCode,`roleDesc`
        from `Role`
        where roleId = #{keyId}
    </select>

    <select id="getUserRolesByUserId" resultMap="RoleResultMap">
        select r.roleId,r.roleCode,r.`roleDesc`
        from `Role` r
        left join `UserRole` ur ON r.RoleId = ur.RoleId
        where ur.UserId = #{keyId}
    </select>

    <insert id="insert" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean">
        insert into `Role`(roleId,`roleCode`,`roleDesc`)
        values(#{roleId},#{roleCode},#{roleDesc})
    </insert>

    <update id="update" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean">
        update `Role`
        <set>
            <if test="roleCode!=null"> `roleCode`=#{roleCode}, </if>
            <if test="roleDesc!=null"> `roleDesc`=#{roleDesc}, </if>
        </set>
        where roleId = #{roleId}
    </update>

    <delete id="delete" parameterType="int">
        delete from `Role` where roleId = #{keyId}
    </delete>

</mapper>

user.mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.it_laowu.springbootstudy.springbootstudydemo.dao.UserDao">
    <resultMap id="UserResultMap" type="com.it_laowu.springbootstudy.springbootstudydemo.bean.UserBean">
        <result column="userId" property="userId"/>
        <result column="userName" property="userName"/>
        <result column="passWord" property="passWord"/>
        <result column="lockedFlag" property="lockedFlag"/>
    </resultMap>

    <select id="findAll" resultMap="UserResultMap">
        select userId,userName,`passWord`,lockedFlag
        from `Users`
        <where>
            <if test="conditionQC.userId != 0">
                and userId = #{conditionQC.userId}
            </if>
            <if test="conditionQC.userName != null and '' != conditionQC.userName">
                and userName like concat('%',#{conditionQC.userName},'%')
            </if>
            <if test="conditionQC.passWord != null and '' != conditionQC.passWord">
                and passWord like concat('%',#{conditionQC.passWord},'%')
            </if>
            <if test="conditionQC.lockedFlag != -1">
                and lockedFlag = #{conditionQC.lockedFlag}
            </if>
        </where>
        <choose>
            <when test="conditionQC.sortSql == null">
                Order by userId
            </when>
            <otherwise>
                ${conditionQC.sortSql}
            </otherwise>
        </choose>
    </select>

    <select id="findOne" resultMap="UserResultMap">
        select userId,userName,`passWord`,lockedFlag
        from `Users`
        where userId = #{keyId}
    </select>
    <select id="findByName" resultMap="UserResultMap">
        select userId,userName,`passWord`,lockedFlag
        from `Users`
        where userName = #{username}
    </select>    

    <insert id="insert" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.UserBean">
        insert into `Users`(userId,`userName`,`passWord`,lockedFlag)
        values(#{userId},#{userName},#{passWord},#{lockedFlag})
    </insert>

    <update id="update" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.UserBean">
        update `Users`
        <set>
            <if test="userName!=null"> `userName`=#{userName}, </if>
            <if test="passWord!=null"> `passWord`=#{passWord}, </if>
            <if test="lockedFlag!=null"> `lockedFlag`=#{lockedFlag}, </if>
        </set>
        where userId = #{userId}
    </update>

    <delete id="delete" parameterType="int">
        delete from `Users` where userId = #{keyId}
    </delete>

</mapper>

UserRole.mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.it_laowu.springbootstudy.springbootstudydemo.dao.UserRoleDao">
    <resultMap id="UserRoleResultMap" type="com.it_laowu.springbootstudy.springbootstudydemo.bean.UserRoleBean">
        <result column="userRoleId" property="userRoleId"/>
        <result column="userId" property="userId"/>
        <result column="roleId" property="roleId"/>
    </resultMap>

    <select id="findAll" resultMap="UserRoleResultMap">
        select userRoleId,userId,`roleId`
        from `UserRole`
        <where>
            <if test="conditionQC.userRoleId != 0">
                and userRoleId = #{conditionQC.userRoleId}
            </if>
            <if test="conditionQC.userId != 0">
                and userId = #{conditionQC.userId}
            </if>
            <if test="conditionQC.roleId != 0">
                and roleId = #{conditionQC.roleId}
            </if>
        </where>
        <choose>
            <when test="conditionQC.sortSql == null">
                Order by userRoleId
            </when>
            <otherwise>
                ${conditionQC.sortSql}
            </otherwise>
        </choose>
    </select>

    <select id="findOne" resultMap="UserRoleResultMap">
        select userRoleId,userId,`RoleId`
        from `UserRole`
        where userRoleId = #{keyId}
    </select>

    <insert id="insert" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.UserRoleBean">
        insert into `UserRole`(`userId`,`roleId`)
        values(#{userId},#{roleId})
    </insert>

    <update id="update" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.UserRoleBean">
        update `UserRole`
        <set>
            <if test="userId!=0"> `userId`=#{userId}, </if>
            <if test="roleId!=0"> `roleId`=#{roleId}, </if>
        </set>
        where userRoleId = #{userRoleId}
    </update>

    <delete id="delete" parameterType="int">
        delete from `UserRole` where userRoleId = #{keyId}
    </delete>

</mapper>

Controller

因為Controller越來越多绊诲,而且都是實現(xiàn)Crud的功能,重復使用的頻率很高褪贵,所以做了個BaseController

package com.it_laowu.springbootstudy.springbootstudydemo.core.base;

import java.util.List;

import com.it_laowu.springbootstudy.springbootstudydemo.core.customexceptions.ConflictException;
import com.it_laowu.springbootstudy.springbootstudydemo.core.pagehelper.PageInfo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

public abstract class BaseController<Bean,Condition,IService extends IBaseService<Bean,Condition>> {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired(required = false)
    public IService baseService;

    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public List<Bean> orders(Condition condition,PageInfo pageinfo) {
        return baseService.findAll(condition,pageinfo);
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public Bean order(@PathVariable(value = "id") int id) {
        return baseService.findOne(id);
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public int add(@Validated @RequestBody Bean bean) {
        return baseService.insert(bean);
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
    public int update(@PathVariable(value = "id") int id, @RequestBody Bean bean) {
        setBeanKeyId(bean, id);
        return baseService.update(bean);
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public int delete(@PathVariable(value = "id") int id) {
        return baseService.delete(id);
    }

    //其實就是update時掂之,設置一下主鍵
    protected abstract void setBeanKeyId(Bean bean,int keyId) ;
}

有了這個,其他Controller就簡單了脆丁,比如:

@RestController
@RequestMapping(value = "/user")
public class UserController extends BaseController<UserBean,UserCondition,IUserService>{
    @Override
    protected void setBeanKeyId(UserBean bean, int keyId) {
        bean.setUserId(keyId);
    }
}

維護用戶數(shù)據(jù)時世舰,記得加密密碼。

這里提供一個密碼 admin偎快,加密后:$2a$10$xQI1JKsTrkemGxEK1mpsAezGQeTDpSNVjfMCLrhgf8ImpCE2hKI1S

maven依賴

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

springboot具有自動裝配機制冯乘,只要引入依賴,即使沒配置晒夹,也會啟動spring security裆馒,配置一套默認的權限。
現(xiàn)在啟動項目丐怯,Console會自動生成用戶User的密碼喷好,訪問API會彈出登錄提示界面,登錄后可以自由訪問頁面读跷。

自動生成密碼

Security代碼

以上都是數(shù)據(jù)庫維護用戶權限數(shù)據(jù)的代碼梗搅。

下面的代碼是集成Spring Security會用到的一些主要的代碼:

  1. WebSecurityConfigurerAdapter:實現(xiàn)Security的各種配置
  2. AuthenticationProvider:通過重寫authenticate方法,實現(xiàn)用戶的校驗效览。
  3. UserDetailsService:通過重寫loadUserByUsername方法无切,可以實現(xiàn)在數(shù)據(jù)庫中配置用戶權限。
  4. MyAuthenticationFailureHandler丐枉、MyAuthenticationSuccessHandler:登錄成功/失敗反饋哆键。
  5. resources/static/auth/login.html:自制比較友好的登錄頁面。
  6. resources/static/auth/logout.html:自制比較友好的登出提示頁面瘦锹。
  7. AdminController:設置了登錄權限的一個頁面籍嘹,提供完整的訪問測試闪盔。

配置文件WebSecurityConfigurerAdapter

這個類提供了Security的各種開關、特性的配置辱士。

首先泪掀,新建一個WebSecurityConfigurerAdapter的實現(xiàn)類:

package com.it_laowu.springbootstudy.springbootstudydemo.core.config;

import com.it_laowu.springbootstudy.springbootstudydemo.core.auth.MyAuthenticationFailureHandler;
import com.it_laowu.springbootstudy.springbootstudydemo.core.auth.MyAuthenticationProvider;
import com.it_laowu.springbootstudy.springbootstudydemo.core.auth.MyAuthenticationSuccessHandler;

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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyAuthenticationProvider myAuthenticationProvider;
    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(myAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        http.httpBasic()
            .and()
        .authorizeRequests()
            .antMatchers("/user/**").permitAll()
            .antMatchers("/role/**").permitAll()
            .antMatchers("/userrole/**").permitAll()
            .antMatchers("/order/**").permitAll()
            .antMatchers("/auth/**").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            //默認是/login,不用改
            .loginPage("/login")
            .successHandler(myAuthenticationSuccessHandler)
            .failureHandler(myAuthenticationFailureHandler)
            .permitAll()
            .and()
        .logout()
            //默認為/logout颂碘,不用改
            .logoutUrl("/logout")  
            .logoutSuccessUrl("/auth/logout.html") //默認跳轉到 /login
            .deleteCookies("JSESSIONID");   //默認也是會刪除cookie的
     }
}
}

針對這部分代碼做一些簡單的說明:

  • authorizeRequests:規(guī)定了哪些頁面需要特定權限异赫,哪些需要用戶登錄,哪些不需要凭涂。
  • formLogin:定義了API登錄頁面祝辣,以及成功失敗的反饋處理(Handler)
  • logout:和login一樣,大部分使用默認參數(shù)即可切油。
  • passwordEncoder:注入一個加密器蝙斜,之前提供的密碼,也是用這個加密器加密的澎胡。

認證相關代碼

MyAuthenticationProvider:用戶認證

package com.it_laowu.springbootstudy.springbootstudydemo.core.auth;
......省略import

@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private MyUserDetailsService myUserDetailsService;
    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        //根據(jù)輸入的用戶密碼孕荠,讀取數(shù)據(jù)庫中信息
        String username = authentication.getName();
        String password = (String) authentication.getCredentials();
        MyUserDetails user = (MyUserDetails) myUserDetailsService.loadUserByUsername(username);

        //判斷是否有效用戶
        if (!user.isEnabled()) {
            throw new DisabledException("USER DISABLE");
        } else if (!user.isAccountNonLocked()) {
            throw new LockedException("USER LOCKED");
        } else if (!user.isAccountNonExpired()) {
            throw new AccountExpiredException("USER EXPIRED");
        } else if (!user.isCredentialsNonExpired()) {
            throw new CredentialsExpiredException("USER LOGOUT");
        }

        //驗證密碼
        if (!passwordEncoder.matches(password, user.getPassword())) {
            throw new BadCredentialsException("PASSWORD INVALID!");
        }

        logger.info(String.format("用戶%s登錄成功", username));

        return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

MyUserDetailsService:用戶數(shù)據(jù)處理,這里是和數(shù)據(jù)庫交互的關鍵點

package com.it_laowu.springbootstudy.springbootstudydemo.core.auth;
......省略import
@Component
public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    private IUserService userService;
    @Autowired
    private IRoleService roleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserBean bean =  userService.findByName(username);
        if (bean == null){
            throw new UsernameNotFoundException("User "+ username +" didn't exist.");
        }
        //獲得角色信息
        List<RoleBean> roles = roleService.getUserRolesByUserId(bean.getUserId());
        //格式轉化
        List<GrantedAuthority> authority = roles.stream().map(i->new SimpleGrantedAuthority(i.getRoleCode())).collect(Collectors.toList());
        return new MyUserDetails(bean.getUserName(),bean.getPassWord(),bean.getLockedFlag()==1,authority);
    }
}

MyUserDetails:UserDetails實現(xiàn)類攻谁,保存數(shù)據(jù)

package com.it_laowu.springbootstudy.springbootstudydemo.core.auth;

import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

@SuppressWarnings("serial")
public class MyUserDetails implements UserDetails {

    protected Integer id;
    private String username;
    private String password;
    private boolean lockedFlag;

    public MyUserDetails(String username, String password, boolean lockedFlag,Collection<? extends GrantedAuthority> roles) {
        this.username = username;
        this.password = password;
        this.lockedFlag = lockedFlag;
        this.authorities = roles;
    }

    public Collection<? extends GrantedAuthority> authorities;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }
    @Override
    public String getPassword() {
        return password;
    }
    @Override
    public String getUsername() {
        return username;
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return  !lockedFlag;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
}

MyAuthenticationSuccessHandler:認證成功反饋

package com.it_laowu.springbootstudy.springbootstudydemo.core.auth;
......省略import
@Component
public class MyAuthenticationSuccessHandler  extends SavedRequestAwareAuthenticationSuccessHandler{
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
        //登錄成功返回
        ResultBody resultBody = new ResultBody("200", "登錄成功");
        //設置返回請求頭
        response.setContentType("application/json;charset=utf-8");
        //寫出流
        PrintWriter out = response.getWriter();
        ObjectMapper mapper = new ObjectMapper();  
        out.write(mapper.writeValueAsString(resultBody));
        out.flush();
        out.close();
    }
}

MyAuthenticationFailureHandler:認證失敗反饋

package com.it_laowu.springbootstudy.springbootstudydemo.core.auth;
......省略import
@Component
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        //登錄失敗信息返回
        ResultBody resultBody = new ResultBody("401", "登錄失敗:"+exception.getLocalizedMessage());
        //設置返回請求頭
        response.setContentType("application/json;charset=utf-8");
        //寫出流
        PrintWriter out = response.getWriter();
        ObjectMapper mapper = new ObjectMapper();  
        out.write(mapper.writeValueAsString(resultBody));
        out.flush();
        out.close();
    }
}

雜項代碼

/resources/static/auth/login.html

就是提供一個友好的登錄頁面稚伍,方便測試。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>

<body>

    <h2>Login</h2>
    <form id="loginForm" action="/login" method="post">
        用戶:<input type="text" id="username" name="username"><br /><br />
        密碼:<input type="password" id="password" name="password"><br /><br />
        <button id="btnlogin" type="button">Login</button>
    </form>
    <span id="msg" name="msg"></h2>

    <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
    <script type="text/javascript">

        $("#btnlogin").click(function () {
            $.ajax({
                type: "POST",
                url: "/springboot-study-demo/login",
                data: $("#loginForm").serialize(),
                dataType: "JSON",
                success: function (data) {
                    console.log(data);
                    $("#msg").text(data.message);
                }
            });

        });

    </script>
</body>

</html>

/resources/static/auth/logout.html

登出成功的反饋頁面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Logout</title>
</head>
<body>
    <h2>Log out success !!</h2>
</body>
</html>

AdminController

業(yè)務Controller戚宦,所有url均需登錄用戶个曙。

@RestController
@RequestMapping(value = "/admin")
public class AdminController {

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String info(@PathVariable(value = "id") int id) {
        return "i am an administrator";
    }
}

測試一下

  • 訪問/admin/1,直接跳轉到/login受楼,訪問失敗
  • 訪問/auth/login.html垦搬,先測密碼錯誤,再測成功艳汽。
  • 訪問/admin/1猴贰,返回i am an administrator
  • 訪問/logout河狐,跳轉到/auth/logout.html.
  • 訪問/admin/1米绕,直接跳轉到/login再次訪問失敗

這里要注意馋艺,部分瀏覽器(比如Chrome)會自動記錄用戶密碼栅干,導致logout后,再次訪問/admin/1還是成功的捐祠。使用火狐就沒這個問題碱鳞。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市雏赦,隨后出現(xiàn)的幾起案子劫笙,更是在濱河造成了極大的恐慌,老刑警劉巖星岗,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件填大,死亡現(xiàn)場離奇詭異,居然都是意外死亡俏橘,警方通過查閱死者的電腦和手機允华,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寥掐,“玉大人靴寂,你說我怎么就攤上這事≌僭牛” “怎么了百炬?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長污它。 經(jīng)常有香客問我剖踊,道長,這世上最難降的妖魔是什么衫贬? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任德澈,我火速辦了婚禮,結果婚禮上固惯,老公的妹妹穿的比我還像新娘梆造。我一直安慰自己,他們只是感情好葬毫,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布镇辉。 她就那樣靜靜地躺著,像睡著了一般供常。 火紅的嫁衣襯著肌膚如雪摊聋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天栈暇,我揣著相機與錄音麻裁,去河邊找鬼。 笑死源祈,一個胖子當著我的面吹牛煎源,可吹牛的內容都是我干的。 我是一名探鬼主播香缺,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼手销,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了图张?” 一聲冷哼從身側響起锋拖,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤诈悍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后兽埃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侥钳,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年柄错,在試婚紗的時候發(fā)現(xiàn)自己被綠了舷夺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡售貌,死狀恐怖给猾,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情颂跨,我是刑警寧澤敢伸,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站毫捣,受9級特大地震影響详拙,放射性物質發(fā)生泄漏。R本人自食惡果不足惜蔓同,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一饶辙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧斑粱,春花似錦弃揽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至尚揣,卻和暖如春涌矢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背快骗。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工娜庇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人方篮。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓名秀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親藕溅。 傳聞我的和親對象是個殘疾皇子匕得,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內容